Python モジュールを拡張するにはどうすればよいですか?`python-twitter` パッケージに新しい機能を追加する

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

質問

既存の Python モジュールを拡張するためのベスト プラクティスは何ですか。この場合、 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的) 方法:

* を python-twitter からモジュールにインポートしてから拡張します。

例えば ​​:

ベースモジュール.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"

実行スクリプト.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 の二重インポートが Python かどうかはわかりません。削除することはできますが、そうすると、basemodule の名前空間にあった関数を拡張するというユースケースに対処できなくなります。

拡張クラスに関しては、Twitter API モジュールを拡張する新しい API(basemodule.API) クラスを作成するだけです。

他のヒント

モジュールに追加しないでください。サブクラス拡張したいクラスを独自のモジュールでサブクラスを使用し、元のものをまったく変更しません。

実行時にモジュールリストを直接操作する方法は次のとおりです。 スポイラー警告: からモジュールタイプを取得します 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 - 私は具体的には、このタイプのこの1つのソースを非自明であるため、具体的に指摘しました。他の多くのビルトインタイプとは異なり、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"""

新しいオブジェクトが交換する機能を提供する場合(または、新しい機能を古いコードベースにバックポートする可能性があります)、名前を上書きすることができます

ここでホイールを再発明しないことをお勧めしますか?私は今2か月間6KラインのTwitterクライアントを除いて構築していますが、最初はPython-Twitterもチェックしましたが、最近のAPIの変更の遅れに遅れをとっていますが、開発もそれほどアクティブではないようです。 (少なくとも私が最後にチェックしたとき)OAUTH/XAUTHのサポートはありません)。

それで、もう少し検索した後、私はTweepyを発見しました:
http://github.com/joshthecoder/tweepy

長所:Active Development、Oaauth/Xauth、および最新のAPI。
あなたが必要とするものがすでにそこにある可能性が高いです。

だから私はそれを使って行くことをお勧めします、それは私のために働いています、私が追加しなければならなかった唯一のことはXauthでした(それはTweepyにマージされました:)

ああ、恥知らずなプラグ、ツイートを解析したり、htmlにフォーマットする必要がある場合は、Twitter-Text-*ライブラリの私のPythonバージョンを使用します。
http://github.com/bonsaiden/twitter-text-python

このことは、twitter.comがするように、ツイートを解析することが保証されているUnittestetdです。

新しいクラスを定義し、元のモジュールから拡張するクラスから継承する代わりに、元のクラスのインスタンスを新しいクラスの属性として追加します。そして、ここにトリックがあります:新しいクラスで存在しないすべてのメソッド呼び出しを傍受し、古いクラスのインスタンスでそれを呼び出すようにしてください。あなたの新しいクラスで、あなたが好きなように新しいまたはオーバーライドされた方法を定義するだけです:

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