web-dev-qa-db-ja.com

メソッドのデコレータ名を取得するための内省?

メソッド上のすべてのデコレータの名前を取得する方法を理解しようとしています。メソッド名とdocstringはすでに取得できますが、デコレータのリストを取得する方法がわかりません。

32
Tony

デコレータの呼び出し方法を変更できる場合

class Foo(object):
    @many
    @decorators
    @here
    def bar(self):
        pass

class Foo(object):
    @register(many,decos,here)
    def bar(self):
        pass

次に、次の方法でデコレータを登録できます。

def register(*decorators):
    def register_wrapper(func):
        for deco in decorators[::-1]:
            func=deco(func)
        func._decorators=decorators        
        return func
    return register_wrapper

例えば:

def many(f):
    def wrapper(*args,**kwds):
        return f(*args,**kwds)
    return wrapper

decos = here = many

class Foo(object):
    @register(many,decos,here)
    def bar(self):
        pass

foo=Foo()

ここで、デコレータのタプルにアクセスします。

print(foo.bar._decorators)
# (<function many at 0xb76d9d14>, <function decos at 0xb76d9d4c>, <function here at 0xb76d9d84>)

ここでは、デコレータの名前だけを印刷します。

print([d.func_name for d in foo.bar._decorators])
# ['many', 'decos', 'here']
27
unutbu

この質問が非常に古く、これを行うための実際の内省的な方法を追加するのに時間がかかっていないことに驚いています。

検査したいコード...

def template(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

baz = template
che = template

class Foo(object):
    @baz
    @che
    def bar(self):
        pass

これで、上記のFooクラスを次のように検査できます...

import ast
import inspect

def get_decorators(cls):
    target = cls
    decorators = {}

    def visit_FunctionDef(node):
        decorators[node.name] = []
        for n in node.decorator_list:
            name = ''
            if isinstance(n, ast.Call):
                name = n.func.attr if isinstance(n.func, ast.Attribute) else n.func.id
            else:
                name = n.attr if isinstance(n, ast.Attribute) else n.id

            decorators[node.name].append(name)

    node_iter = ast.NodeVisitor()
    node_iter.visit_FunctionDef = visit_FunctionDef
    node_iter.visit(ast.parse(inspect.getsource(target)))
    return decorators

print get_decorators(Foo)

それはこのようなものを印刷するはずです...

{'bar': ['baz', 'che']}

または、少なくともこれをPython 2.7.9 real quick :)でテストしたときに実行されました。

35
Jaymon

これは、デコレータが「シンタックスシュガー」であるためです。次のデコレータがあるとします。

def MyDecorator(func):
    def transformed(*args):
        print "Calling func " + func.__name__
        func()
    return transformed

そして、それを関数に適用します。

@MyDecorator
def thisFunction():
    print "Hello!"

これは次と同等です。

thisFunction = MyDecorator(thisFunction)

デコレータを制御している場合は、関数オブジェクトに「履歴」を埋め込むことができます。これを行うための賢い方法は他にもあると思いますが(おそらく割り当てをオーバーライドすることによって)、Python残念ながら、私はそれほど精通していません。:(

3
Faisal

Faisal notes のように、デコレータ自体にメタデータを関数に添付させることもできますが、私の知る限り、それは自動的には行われません。

2

できませんが、さらに悪いことに、最初に関数を装飾したという事実を隠すのに役立つライブラリが存在します。 Functools またはデコレータライブラリ(@decorator私がそれを見つけることができれば)詳細については。

1
wheaties

私の意見ではそれは不可能です。デコレータは、メソッドのある種の属性またはメタデータではありません。デコレータは、関数を関数呼び出しの結果に置き換えるための便利な構文です。詳細については、 http://docs.python.org/whatsnew/2.4.html?highlight=decorators#pep-318-decorators-for-functions-and-methods を参照してください。

0
Achim

一般的な方法で行うことは不可能です。

@foo
def bar ...

とまったく同じです

def bar ...
bar = foo (bar)

おそらく@staticmethodのように、関数オブジェクトを分析することによってそれを行うことができますが、それよりも優れているわけではありません。

0
doublep