Python何时执行装饰器

Posted by clannadzsy  ‐ Updated  ‐  1 min read

运行环境:CPython 3.12.14,只需标准库。将第一个代码块保存为一个空目录中的 decorator.py,在该目录执行 python decorator.py,即可看到第一组输出。输出里的函数地址每次可能不同,应核对行的顺序和函数名。

  1. 执行到函数定义语句时,装饰器会接收刚创建的函数对象,并将返回值绑定到函数名;这通常发生在模块首次导入时。被装饰函数的函数体仍要等到调用才执行。本例装饰器只注册并返回原函数,并未在注册时调用 f1() 或 f2(),参见函数定义的官方说明。
# decorator.py

decorators = []


def decorator(func):
    print(f'running decorator {func}')
    decorators.append(func)
    return func


@decorator
def f1():
    print('running f1()')


@decorator
def f2():
    print('running f2()')


def f3():
    print('running f3()')


def main():
    print('running main()')
    print('decorators ->', decorators)
    f1()
    f2()
    f3()


if __name__ == '__main__':
    main()

running decorator <function f1 at 0x000002AC46B37550>
running decorator <function f2 at 0x000002AC46B55C10>
running main()
decorators -> [<function f1 at 0x000002AC46B37550>, <function f2 at 0x000002AC46B55C10>]
running f1()
running f2()
running f3()

2. 当 decorator.py 被导入,而不是作为脚本运行

下面的代码在同一目录内另存为 import_demo.py,用一个新的 Python 进程执行 python import_demo.py。不能将它追加到 decorator.py 自身,否则脚本会再以模块名加载一遍,混淆这两种运行方式。

import decorator

print(decorator.decorators)
assert [func.__name__ for func in decorator.decorators] == ['f1', 'f2']

# running decorator <function f1 at 0x000001BBC3585C10>
# running decorator <function f2 at 0x000001BBC3586310>
# [<function f1 at 0x000001BBC3585C10>, <function f2 at 0x000001BBC3586310>]

导入时会执行注册语句,但 if __name__ == '__main__' 不成立,所以不会运行 main()。在同一进程中再次普通 import decorator 通常会复用 sys.modules 中的模块,不会再次执行装饰器;这里用新进程来对照首次导入的输出。