web-dev-qa-db-ja.com

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

既存のPythonモジュールを拡張するためのベストプラクティスは何ですか?この場合、基本APIクラスに新しいメソッドを追加してpython-Twitterパッケージを拡張したいと思います。

tweepyを見てきましたが、それも気に入っています。 python-Twitterが理解しやすく、必要な機能で拡張できると思います。

私はすでにメソッドを作成しています。このモジュールのコアを変更せずに、メソッドをpython-Twitterパッケージモジュールに追加するための最もPython的で中断の少ない方法を見つけようとしています。

20
user319045

いくつかの方法。

簡単な方法:

モジュールを拡張せず、クラスを拡張します。

exttwitter.py

import Twitter

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

欠点:Twitterのすべてのクラスは、(上記のように)単なるスタブであっても、exttwitter.pyに含まれている必要があります

より難しい(おそらく非Pythonic)方法:

*を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の二重インポートがPythonicであるかどうかはわかりません。削除することはできますが、basemoduleの名前空間にあった関数を拡張したいというユースケースを処理できません。

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

21
Rizwan Kassim

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

5
Mike Graham

実行時にモジュールリストを直接操作する方法は次のとおりです– spoiler alert:typesmoduleからモジュールタイプを取得します。

_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__}")
_

…もちろん、新しく宣言したclassの祖先として_types.ModuleType_を指定することにより、独自のモジュールサブクラスを作成することもできます。私は個人的にこれが必要だと思ったことはありません。

(また、typesモジュールからモジュールタイプを取得するためにhaveを実行する必要はありません。osをインポートした後はいつでもModuleType = type(os)のようなことを行うことができます。 –このタイプのソースは自明ではないため、特に指摘しました。他の多くの組み込みタイプとは異なり、Pythonは、グローバル名前空間のモジュールタイプへのアクセスを提供しません。 。)

実際のアクションは_sys.modules_ dictにあり、(適切に勇気がある場合は)既存のモジュールを置き換えたり、新しいモジュールを追加したりできます。

5
fish2000

次のように使用する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"""

新しいオブジェクトが置き換えようとしている機能を提供している場合(または新しい機能を古いコードベースにバックポートしている場合)、名前を上書きできます

3
Aaron Hall

ここで車輪の再発明をしないことを提案してもいいですか?私は2か月間> 6kラインのTwitterクライアントを構築しています。最初はpython-Twitterもチェックしましたが、最近のAPIの変更よりも大幅に遅れています。開発もそれほど活発ではないようです。 (少なくとも最後にチェックしたとき)OAuth/xAuthはサポートされていません)。

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

長所:アクティブな開発、OAauth/xAuth、および最新のAPI。
必要なものがすでにそこにある可能性が高いです。

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

恥知らずなプラグインです。ツイートを解析したり、HTMLにフォーマットしたりする必要がある場合は、私のpythonバージョンのTwitter-text- *ライブラリを使用してください。
http://github.com/BonsaiDen/Twitter-text-python

これは、Twitter.comと同じようにツイートを解析することが保証されているunittestetdです。

2
Ivo Wetzel

新しいクラスを定義し、元のモジュールから拡張するクラスから継承する代わりに、元のクラスのインスタンスを属性として新しいクラスに追加します。そして、ここに秘訣があります。新しいクラスで存在しないすべてのメソッド呼び出しをインターセプトし、古いクラスのインスタンスでそれを呼び出そうとします。 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です。)

0
gOldie_E36