كيف يمكنني تمديد وحدة بيثون؟ إضافة وظائف جديدة إلى حزمة "بيثون تويتر"

StackOverflow https://stackoverflow.com/questions/2705964

سؤال

ما هي أفضل الممارسات لتوسيع وحدة بيثون الموجودة - في هذه الحالة ، أريد تمديد python-twitter حزمة عن طريق إضافة طرق جديدة إلى فئة API الأساسية.

لقد نظرت إلى tweepy, ، وأنا أحب ذلك أيضًا ؛ أنا فقط أجد python-twitter أسهل في الفهم والتمديد مع الوظيفة التي أريدها.

لدي الطرق المكتوبة بالفعل - أحاول معرفة الطريقة الأكثر إثارة والأقل إزعاجًا لإضافتها إلى python-twitter الوحدة النمطية ، دون تغيير جوهر هذه الوحدات.

هل كانت مفيدة؟

المحلول

بعض الطرق.

الطريق السهل:

لا تمدد الوحدة ، وتمديد الفصول.

exttwitter.py

import twitter

class Api(twitter.Api):
    pass 
    # override/add any functions here.

الجانب السلبي: يجب أن يكون كل فصل في Twitter في exttwitter.py ، حتى لو كان مجرد كعب (على النحو الوارد أعلاه)

طريقة أصعب (ربما غير متوفرة):

استيراد * من Python-twitter إلى وحدة نمطية تمتد بعد ذلك.

على سبيل المثال :

basemodule.py

 class Ball():
    def __init__(self,a):
        self.a=a
    def __repr__(self):
        return "Ball(%s)" % self.a

def makeBall(a):
    return Ball(a)

def override():
    print "OVERRIDE ONE"

def dontoverride():
    print "THIS WILL BE PRESERVED"

extmodule.py

from basemodule import *
import basemodule

def makeBalls(a,b):
    foo = makeBall(a)
    bar = makeBall(b)
    print foo,bar

def override():
    print "OVERRIDE TWO"

def dontoverride():
    basemodule.dontoverride()
    print "THIS WAS PRESERVED"

Runscript.py

import extmodule

#code is in extended module
print extmodule.makeBalls(1,2)
#returns Ball(1) Ball(2)

#code is in base module
print extmodule.makeBall(1)
#returns Ball(1)

#function from extended module overwrites base module
extmodule.override()
#returns OVERRIDE TWO

#function from extended module calls base module first
extmodule.dontoverride()
#returns THIS WILL BE PRESERVED\nTHIS WAS PRESERVED

لست متأكدًا مما إذا كان الاستيراد المزدوج في extmodule.py هو بيثوني - يمكنك إزالته ، ولكن بعد ذلك لا تتعامل مع usecase من الرغبة في تمديد وظيفة كانت في مساحة اسم القاعدة.

بقدر الفصول الممتدة ، ما عليك سوى إنشاء فئة جديدة من API (basemodule.api) لتمديد وحدة API Twitter.

نصائح أخرى

لا تضيفهم إلى الوحدة النمطية. الفئة الفرعية الفئات التي تريد تمديدها واستخدام فئاتك الفرعية في الوحدة النمطية الخاصة بك ، وعدم تغيير الأشياء الأصلية على الإطلاق.

إليك كيف يمكنك معالجة قائمة الوحدات النمطية مباشرة في وقت التشغيل - تنبيه المفسد: تحصل على نوع الوحدة من types وحدة:

from __future__ import print_function
import sys
import types
import typing as tx

def modulize(namespace: tx.Dict[str, tx.Any],
             modulename: str,
             moduledocs: tx.Optional[str] = None) -> types.ModuleType:

    """ Convert a dictionary mapping into a legit Python module """

    # Create a new module with a trivially namespaced name:
    namespacedname: str = f'__dynamic_modules__.{modulename}'
    module = types.ModuleType(namespacedname, moduledocs)
    module.__dict__.update(namespace)

    # Inspect the new module:
    name: str = module.__name__
    doc: tx.Optional[str] = module.__doc__
    contents: str = ", ".join(sorted(module.__dict__.keys()))
    print(f"Module name:      {name}")
    print(f"Module contents:  {contents}")
    if doc:
        print(f"Module docstring: {doc}")

    # Add to sys.modules, as per import machinery:
    sys.modules.update({ modulename : module })

    # Return the new module instance:
    return module

... يمكنك بعد ذلك استخدام مثل هذه الوظيفة مثل:

ns = {
         'func' : lambda: print("Yo Dogg"), # these can also be normal non-lambda funcs
    'otherfunc' : lambda string=None: print(string or 'no dogg.'),
      '__all__' : ('func', 'otherfunc'),
      '__dir__' : lambda: ['func', 'otherfunc'] # usually this’d reference __all__
}

modulize(ns, 'wat', "WHAT THE HELL PEOPLE")
import wat

# Call module functions:
wat.func()
wat.otherfunc("Oh, Dogg!")

# Inspect module:
contents = ", ".join(sorted(wat.__dict__.keys()))
print(f"Imported module name:      {wat.__name__}")
print(f"Imported module contents:  {contents}")
print(f"Imported module docstring: {wat.__doc__}")

... يمكنك أيضًا إنشاء الفئة الفرعية للوحدة الخاصة بك ، من خلال تحديد types.ModuleType كجداد الخاص بك المعلن حديثا class, ، بالطبع؛ لم أجد شخصيًا هذا ضروريًا.

(أيضا ، أنت لا تفعل لديك للحصول على نوع الوحدة من types الوحدة النمطية - يمكنك دائمًا فعل شيء مثل ModuleType = type(os) بعد الاستيراد os -لقد أشرت على وجه التحديد إلى هذا المصدر الوحيد من النوع لأنه غير مؤهل ؛ على عكس العديد من أنواعها الأخرى المصممة ، لا يوفر Python الوصول إلى نوع الوحدة النمطية في مساحة الاسم العالمية.)

العمل الحقيقي في sys.modules DICT ، حيث (إذا كنت معقلاً بشكل مناسب) ، يمكنك استبدال الوحدات النمطية الموجودة وكذلك إضافة لوحاتك الجديدة.

قل أن لديك وحدة أقدم تسمى mod التي تستخدمها مثل هذا:

import mod

obj = mod.Object()
obj.method()
mod.function()
# and so on...

وتريد تمديده ، دون استبداله للمستخدمين. القيام به بسهولة. يمكنك إعطاء الوحدة الجديدة الخاصة بك اسمًا مختلفًا ، newmod.py أو ضعه بنفس الاسم في مسار أعمق واحتفظ بنفس الاسم ، على سبيل المثال /path/to/mod.py. ثم يمكن للمستخدمين استيراده في أي من هذه الطرق:

import newmod as mod       # e.g. import unittest2 as unittest idiom from Python 2.6

أو

from path.to import mod    # useful in a large code-base

في الوحدة النمطية الخاصة بك ، ستحتاج إلى إتاحة جميع الأسماء القديمة:

from mod import *

أو بتسمية كل اسم تستورده بشكل صريح:

from mod import Object, function, name2, name3, name4, name5, name6, name7, name8, name9, name10, name11, name12, name13, name14, name15, name16, name17, name18, name19, name20, name21, name22, name23, name24, name25, name26, name27, name28, name29, name30, name31, name32, name33, name34, name35, name36, name37, name38, name39

اعتقد ان import * سيكون أكثر قابلية للصيانة في حالة الاستخدام هذه - إذا كانت الوحدة الأساسية توسع وظائفها ، فستتابع بسلاسة (على الرغم من أنك قد تظلم كائنات جديدة بنفس الاسم).

إذا mod أنت تمتد لها لائق __all__, ، سوف يقيد الأسماء المستوردة.

يجب عليك أيضًا إعلان __all__ وتوسيعه مع الوحدة النمطية الممتدة __all__.

import mod
__all__ = ['NewObject', 'newfunction']
__all__ += mod.__all__   
# if it doesn't have an __all__, maybe it's not good enough to extend
# but it could be relying on the convention of import * not importing
# names prefixed with underscores, (_like _this)

ثم تمديد الكائنات والوظائف كما تفعل عادة.

class NewObject(object):
    def newmethod(self):
        """this method extends Object"""

def newfunction():
    """this function builds on mod's functionality"""

إذا كانت الكائنات الجديدة توفر وظائف تنوي استبدالها (أو ربما تقوم بتراجع الوظائف الجديدة إلى قاعدة رمز أقدم) ، يمكنك الكتابة فوق الأسماء

هل لي أن أقترح عدم إعادة اختراع العجلة هنا؟ أقوم ببناء عميل Twitter لخط 6K لمدة شهرين الآن ، في البداية راجعت Python-twitter أيضًا ، لكن الأمر يتخلف كثيرًا عن تغييرات API الأخيرة ، لا يبدو أن التطوير نشط أيضًا ، كما كان هناك أيضًا (على الأقل عندما راجعت آخر مرة) لا يوجد دعم لـ OAUTH/XAUTH).

لذلك بعد البحث أكثر قليلاً اكتشفت Tweepy:
http://github.com/joshthecoder/tweepy

الايجابيات: التنمية النشطة ، OAAUTH/XAUTH وحديث مع واجهة برمجة التطبيقات.
هناك احتمالات عالية أن ما تحتاجه موجود بالفعل هناك.

لذلك أقترح الذهاب مع ذلك ، إنه يعمل بالنسبة لي ، والشيء الوحيد الذي كان علي إضافته هو Xauth (الذي أعاد الاندماج إلى Tweepy :)

يا قابس وقح ، إذا كنت بحاجة إلى تحليل التغريدات و/أو تنسيقها إلى HTML ، فاستخدم إصدار Python الخاص بي من مكتبات Twitter-Text-*:
http://github.com/bonsaiden/twitter-text-python

هذا الشيء هو unittestetd مضمونة لتحليل التغريدات تماما مثل Twitter.com يفعل ذلك.

حدد فئة جديدة ، وبدلاً من ورثها من الفئة التي تريد تمديدها من الوحدة الأصلية ، أضف مثيلًا للفئة الأصلية كسمة لفصلك الجديد. وهنا تأتي الخدعة: اعتراض جميع الطريقة غير الموجودة على صفك الجديد وحاول الاتصال بها على مثيل الفصل القديم. في NewClass الخاص بك ، حدد فقط طرقًا جديدة أو متطورة كما تريد:

import originalmodule

class NewClass:
    def __init__(self, *args, **kwargs):
        self.old_class_instance = originalmodule.create_oldclass_instance(*args, **kwargs)

    def __getattr__(self, methodname):
        """This is a wrapper for the original OldClass class.

        If the called method is not part of this NewClass class,
        the call will be intercepted and replaced by the method
        in the original OldClass instance.
        """
        def wrapper(*args, **kwargs):
            return getattr(self.old_class_instance, methodname)(*args, **kwargs)
        return wrapper

    def new_method(self, arg1):
        """Does stuff with the OldClass instance"""
        thing = self.old_class_instance.get_somelist(arg1)
        # returns the first element only
        return thing[0]

    def overridden_method(self):
        """Overrides an existing method, if OldClass has a method with the same name"""
        print("This message is coming from the NewClass and not from the OldClass")

في حالتي ، استخدمت هذا الحل عندما لم يكن الوراثة البسيطة من الفصل القديم ممكنًا ، لأنه كان لا بد من إنشاء مثيل ليس بواسطة مُنشئه ، ولكن مع برنامج نصي init من فئة/وحدة أخرى. (إنه OriginalModule.create_oldclass_instance في المثال أعلاه.)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top