メソッド上のすべてのデコレータの名前を取得する方法を理解しようとしています。メソッド名とdocstringはすでに取得できますが、デコレータのリストを取得する方法がわかりません。
デコレータの呼び出し方法を変更できる場合
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']
この質問が非常に古く、これを行うための実際の内省的な方法を追加するのに時間がかかっていないことに驚いています。
検査したいコード...
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 :)でテストしたときに実行されました。
これは、デコレータが「シンタックスシュガー」であるためです。次のデコレータがあるとします。
def MyDecorator(func):
def transformed(*args):
print "Calling func " + func.__name__
func()
return transformed
そして、それを関数に適用します。
@MyDecorator
def thisFunction():
print "Hello!"
これは次と同等です。
thisFunction = MyDecorator(thisFunction)
デコレータを制御している場合は、関数オブジェクトに「履歴」を埋め込むことができます。これを行うための賢い方法は他にもあると思いますが(おそらく割り当てをオーバーライドすることによって)、Python残念ながら、私はそれほど精通していません。:(
Faisal notes のように、デコレータ自体にメタデータを関数に添付させることもできますが、私の知る限り、それは自動的には行われません。
できませんが、さらに悪いことに、最初に関数を装飾したという事実を隠すのに役立つライブラリが存在します。 Functools またはデコレータライブラリ(@decorator
私がそれを見つけることができれば)詳細については。
私の意見ではそれは不可能です。デコレータは、メソッドのある種の属性またはメタデータではありません。デコレータは、関数を関数呼び出しの結果に置き換えるための便利な構文です。詳細については、 http://docs.python.org/whatsnew/2.4.html?highlight=decorators#pep-318-decorators-for-functions-and-methods を参照してください。
一般的な方法で行うことは不可能です。
@foo
def bar ...
とまったく同じです
def bar ...
bar = foo (bar)
おそらく@staticmethod
のように、関数オブジェクトを分析することによってそれを行うことができますが、それよりも優れているわけではありません。