Ton*_*ony 32 python introspection decorator
我试图弄清楚如何获取方法上所有装饰器的名称.我已经可以获取方法名称和docstring,但无法弄清楚如何获取装饰器列表.
Jay*_*mon 38
我很惊讶这个问题太老了,没有人花时间添加实际的内省方法,所以这里是:
你要检查的代码......
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
Run Code Online (Sandbox Code Playgroud)
现在你可以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)
Run Code Online (Sandbox Code Playgroud)
应该打印这样的东西......
{'bar': ['baz', 'che']}
Run Code Online (Sandbox Code Playgroud)
或者至少它是在我用Python 2.7.9真正快速测试的时候做的:)
unu*_*tbu 29
如果你可以改变调用装饰器的方式
class Foo(object):
@many
@decorators
@here
def bar(self):
pass
Run Code Online (Sandbox Code Playgroud)
至
class Foo(object):
@register(many,decos,here)
def bar(self):
pass
Run Code Online (Sandbox Code Playgroud)
那么你可以这样注册装饰器:
def register(*decorators):
def register_wrapper(func):
for deco in decorators[::-1]:
func=deco(func)
func._decorators=decorators
return func
return register_wrapper
Run Code Online (Sandbox Code Playgroud)
例如:
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()
Run Code Online (Sandbox Code Playgroud)
在这里我们访问装饰器的元组:
print(foo.bar._decorators)
# (<function many at 0xb76d9d14>, <function decos at 0xb76d9d4c>, <function here at 0xb76d9d84>)
Run Code Online (Sandbox Code Playgroud)
这里我们只打印装饰器的名称:
print([d.func_name for d in foo.bar._decorators])
# ['many', 'decos', 'here']
Run Code Online (Sandbox Code Playgroud)
小智 10
我添加了同样的问题。在我的单元测试中,我只想确保给定的函数/方法使用了装饰器。
装饰器是单独测试的,所以我不需要测试每个装饰函数的通用逻辑,只需使用装饰器即可。
我终于想出了以下帮助函数:
import inspect
def get_decorators(function):
"""Returns list of decorators names
Args:
function (Callable): decorated method/function
Return:
List of decorators as strings
Example:
Given:
@my_decorator
@another_decorator
def decorated_function():
pass
>>> get_decorators(decorated_function)
['@my_decorator', '@another_decorator']
"""
source = inspect.getsource(function)
index = source.find("def ")
return [
line.strip().split()[0]
for line in source[:index].strip().splitlines()
if line.strip()[0] == "@"
]
Run Code Online (Sandbox Code Playgroud)
对于列表理解,它有点“密集”,但它可以解决问题,在我的情况下它是一个测试辅助函数。
如果您只对装饰器名称感兴趣,而不是潜在的装饰器参数,则它会起作用。如果你想支持装饰器接受参数,像line.strip().split()[0].split("(")[0]
这样的东西可以做到这一点(未经测试)
最后,如果您愿意,可以通过替换line.strip().split()[0]
为删除“@”line.strip().split()[0][1:]