web-dev-qa-db-ja.com

各メソッドで何度も入力せずにクラスのすべての関数を装飾する方法は?

私のクラスには多くのメソッドがあり、それぞれにデコレータを適用し、後で新しいメソッドを追加するときに同じデコレータを適用したいが、メソッド宣言の上に@mydecoratorを書きたくない時間?

__call__それは正しい道ですか?

重要:以下の例は、元の質問とは異なる問題を解決しているようです。

編集:この方法を示したいと思います。これは、コメントで述べたようにミックスインを使用して、後でこの質問を見つけた人にとっての私の問題に対する同様の解決策です。

class WrapinMixin(object):
    def __call__(self, hey, you, *args):
        print 'entering', hey, you, repr(args)
        try:
            ret = getattr(self, hey)(you, *args)
            return ret
        except:
            ret = str(e)
            raise
        finally:
            print 'leaving', hey, repr(ret)

その後、別のことができます

class Wrapmymethodsaround(WrapinMixin): 
    def __call__:
         return super(Wrapmymethodsaround, self).__call__(hey, you, *args)
51
rapadura

クラスの属性を調べて呼び出し可能オブジェクトを装飾する関数でクラスを装飾します。呼び出し可能なクラス変数があり、ネストされたクラスを装飾する場合はこれが間違っている可能性があります(これを指摘するのはSven Marnachの功績です)が、一般的にはかなりクリーンでシンプルなソリューションです。実装例(これは、必要な場合とそうでない場合がある特別なメソッド(___init___など)を除外しないことに注意してください):

_def for_all_methods(decorator):
    def decorate(cls):
        for attr in cls.__dict__: # there's propably a better way to do this
            if callable(getattr(cls, attr)):
                setattr(cls, attr, decorator(getattr(cls, attr)))
        return cls
    return decorate
_

次のように使用します。

_@for_all_methods(mydecorator)
class C(object):
    def m1(self): pass
    def m2(self, x): pass
    ...
_

Python 3.0および3.1では、callableは存在しません。それはPython 2.xで永遠に存在し、Python 3.2でisinstance(x, collections.Callable)のラッパーとして戻ってきたので、それを使用(または独自のcallable置換を定義)これらのバージョンでは).

55
user395760

明示的な方法で行う場合に魔法の方法を使用するのは好きではありませんが、おそらくメタクラスを使用できます。

def myDecorator(fn):
    fn.foo = 'bar'
    return fn

class myMetaClass(type):
    def __new__(cls, name, bases, local):
        for attr in local:
            value = local[attr]
            if callable(value):
                local[attr] = myDecorator(value)
        return type.__new__(cls, name, bases, local)

class myClass(object):
    __metaclass__ = myMetaClass
    def baz(self):
        print self.baz.foo

myClassの各呼び出し可能オブジェクトがmyDecoratorで装飾されているかのように動作します

>>> quux = myClass()
>>> quux.baz()
bar

死者から物をよみがえらせるためではないが、私はデルナンの答えが本当に好きだったが、それが欠けていることに気付いた。

def for_all_methods(exclude, decorator):
    def decorate(cls):
        for attr in cls.__dict__:
            if callable(getattr(cls, attr)) and attr not in exclude:
                setattr(cls, attr, decorator(getattr(cls, attr)))
        return cls
    return decorate

編集:インデントを修正

したがって、装飾したくないメソッド//属性//ものを指定できます

8
nickneedsaname

___dict___を使用して達成されなかった継承されたメソッドも装飾したかったため、上記の答えは私には役立ちませんでした。最後に、クラスのすべての関数で使用される時間を測定するためのプロファイリングコードをすぐに追加する必要があるため、Python 2のソリューションがあります。

_import inspect
def for_all_methods(decorator):
    def decorate(cls):
        for name, fn in inspect.getmembers(cls, inspect.ismethod):
            setattr(cls, name, decorator(fn))
        return cls
    return decorate
_

ソース(わずかに異なるソリューション): https://stackoverflow.com/a/3467879/1243926 ここで、Python 3。

他の回答へのコメントが示唆するように、代わりにinspect.getmembers(cls, inspect.isroutine)の使用を検討してください。 Python 2とPython 3で動作し、継承されたメソッドを修飾し、それでも7行で実行できる適切なソリューションを見つけた場合、編集。

4
osa

メタクラスを生成できます。これは、継承されたメソッドを装飾しません。

def decorating_meta(decorator):
    class DecoratingMetaclass(type):
        def __new__(self, class_name, bases, namespace):
            for key, value in list(namespace.items()):
                if callable(value):
                    namespace[key] = decorator(value)

            return type.__new__(self, class_name, bases, namespace)

    return DecoratingMetaclass

これにより、指定された関数ですべてのメソッドを修飾するメタクラスが生成されます。 Python 2または3で次のように使用することができます

def doubling_decorator(f):
    def decorated(*a, **kw):
        return f(*a, **kw) * 2
    return decorated

class Foo(dict):
    __metaclass__ = decorating_meta(doubling_decorator)

    def lookup(self, key):
        return self[key]

d = Foo()
d["bar"] = 5
print(d.lookup("bar")) # prints 10
2
Jeremy Banks