28

特定のクラスのすべてのメソッドをPythonでラップしたいのですが、クラスのコードを最小限に編集することでラップしたいと思います。これについてはどうすればよいですか?

4

3 に答える 3

31

これを行うためのエレガントな方法は、Michael FoordのVoidspaceブログのメタクラスとは何か、およびメタクラスを 装飾するメソッドというタイトルのセクションでそれらを使用する方法についてのエントリで説明されています。少し単純化して状況に適用すると、次のようになります。

from functools import wraps
from types import FunctionType

def wrapper(method):
    @wraps(method)
    def wrapped(*args, **kwargs):
    #   ... <do something to/with "method" or the result of calling it>
    return wrapped

class MetaClass(type):
    def __new__(meta, classname, bases, classDict):
        newClassDict = {}
        for attributeName, attribute in classDict.items():
            if isinstance(attribute, FunctionType):
                # replace it with a wrapped version
                attribute = wrapper(attribute)
            newClassDict[attributeName] = attribute
        return type.__new__(meta, classname, bases, newClassDict)

class MyClass(object):
    __metaclass__ = MetaClass  # wrap all the methods
    def method1(self, ...):
        # ...etc ...

Pythonでは、関数/メソッドデコレータは、関数ラッパーに加えて、それらを簡単に(そしてよりきれいに)使用できるようにするための構文糖衣です。

Python3互換性アップデート

前のコードはPython2.xメタクラス構文を使用しており、Python 3.xで使用するには変換する必要がありますが、以前のバージョンでは機能しなくなります。これは、以下を使用する必要があることを意味します。

class MyClass(metaclass=MetaClass)  # apply method-wrapping metaclass
    ...

それ以外の:

class MyClass(object):
    __metaclass__ = MetaClass  # wrap all the methods
    ...

必要に応じて、Python 2.xと3.xの両方と互換性のあるコードを記述できますが、そのためには、目的のメタクラスを継承する新しい基本クラスを動的に作成する少し複雑な手法を使用する必要があります。 Pythonの2つのバージョン間の構文の違い。これは基本的に、ベンジャミンピーターソンの6つのモジュールのwith_metaclass()機能が行うことです。

from types import FunctionType
from functools import wraps

def wrapper(method):
    @wraps(method)
    def wrapped(*args, **kwargs):
        print('{!r} executing'.format(method.__name__))
        return method(*args, **kwargs)
    return wrapped


class MetaClass(type):
    def __new__(meta, classname, bases, classDict):
        newClassDict = {}
        for attributeName, attribute in classDict.items():
            if isinstance(attribute, FunctionType):
                # replace it with a wrapped version
                attribute = wrapper(attribute)
            newClassDict[attributeName] = attribute
        return type.__new__(meta, classname, bases, newClassDict)


def with_metaclass(meta):
    """ Create an empty class with the supplied bases and metaclass. """
    return type.__new__(meta, "TempBaseClass", (object,), {})


if __name__ == '__main__':

    # Inherit metaclass from a dynamically-created base class.
    class MyClass(with_metaclass(MetaClass)):
        @staticmethod
        def a_static_method():
            pass

        @classmethod
        def a_class_method(cls):
            pass

        def a_method(self):
            pass

    instance = MyClass()
    instance.a_static_method()  # Not decorated.
    instance.a_class_method()   # Not decorated.
    instance.a_method()         # -> 'a_method' executing
于 2012-07-05T18:38:19.193 に答える
9

プログラムでクラスのメソッドにラッパーを設定するという意味ですか?まあ、これはおそらく本当に悪い習慣ですが、これがあなたがそれをするかもしれない方法です:

def wrap_methods( cls, wrapper ):
    for key, value in cls.__dict__.items( ):
        if hasattr( value, '__call__' ):
            setattr( cls, key, wrapper( value ) )

たとえば、クラスがある場合

class Test( ):
    def fire( self ):
        return True
    def fire2( self ):
        return True

とラッパー

def wrapper( fn ):
    def result( *args, **kwargs ):
        print 'TEST'
        return fn( *args, **kwargs )
    return result

その後、呼び出します

wrap_methods( Test, wrapper )

クラスで定義されたすべてのメソッドに適用wrapperされます。注意して使用してください!実際には、まったく使用しないでください。Test

于 2012-07-05T17:24:05.823 に答える
5

デフォルトのクラスの動作を大幅に変更する必要がある場合は、MetaClassesが最適です。別のアプローチがあります。

__getattribute__ユースケースがクラスのインスタンスメソッドのラップのみに限定されている場合は、マジックメソッドをオーバーライドしてみてください。

from functools import wraps
def wrapper(func):
    @wraps(func)
    def wrapped(*args, **kwargs):
        print "Inside Wrapper. calling method %s now..."%(func.__name__)
        return func(*args, **kwargs)
    return wrapped

ラッパーを作成するときは必ず使用してください。functools.wrapsラッパーが適切なトレースバックを提供するため、ラッパーがデバッグ用である場合はさらに使用してください。

import types
class MyClass(object): # works only for new-style classes
    def method1(self):
        return "Inside method1"
    def __getattribute__(self, name):
        attr = super(MyClass, self).__getattribute__(name)
        if type(attr) == types.MethodType:
            attr = wrapper(attr)
        return attr
于 2015-02-11T14:13:22.450 に答える