有没有办法让python函数知道它在模块加载时被装饰?

Is there a way for a python function to know it is getting decorated at module load time?

例如:

1
2
3
4
5
6
7
def decorator(func):
    def nested(*args, **kwargs):
        return func(*args, **kwargs)
    return nested

@decorator
def decorated(): pass

有没有办法让装修人员知道它正在装修?


可以使用使用ast.NodeVistor遍历函数的ast节点的decorator来查找函数的decorator。如果decorator列表包含的内容超过decorator checker本身,则可以从decorator节点获取其他decorator的详细信息:

1
2
3
4
5
6
7
8
9
10
11
12
import inspect
import ast
from textwrap import dedent

class CheckDecorators(ast.NodeVisitor):
    def visit_FunctionDef(self, node):
        if len(node.decorator_list) > 1:
            print("function '%s' is decorated by: %s" % (node.name, ', '.join(ast.dump(decorator) for decorator in node.decorator_list if not isinstance(decorator, ast.Name) or decorator.id != 'check_decorators')))

def check_decorators(func):
    CheckDecorators().visit(ast.parse(dedent(inspect.getsource(func))))
    return func

以便:

1
2
3
4
5
6
7
8
9
def decorator(func):
    def nested(*args, **kwargs):
        return func(*args, **kwargs)
    return nested

@decorator
@check_decorators
def decorated():
    pass

将输出:

1
function 'decorated' is decorated by: Name(id='decorator', ctx=Load())


下面是一个部分解决方案,它适用于与示例完全类似的闭包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import inspect

def decorator(func):
    def nested(*args, **kwargs):
        return func(*args, **kwargs)

    return nested

@decorator
def decorated(): pass

def not_decorated(): pass

print(inspect.getclosurevars(decorated).nonlocals)
print(inspect.getclosurevars(not_decorated).nonlocals)

# => {'func': <function decorated at 0x10e1408c8>}
# => {}

像您这样的修饰函数将有闭包变量,尽管不能保证其他函数不会。

另外,在EDOCX1中还有其他可以玩的东西。另外,如果一开始使用functools.wrap就很容易了(@kindall)。对于类方法,您可以查找如何检测decorator是否已应用于方法或函数?