1-1
from dis import dis
import time
def clock(func):
def clocked(*args):
t0 = time.perf_counter()
result = func(*args)
elasped = time.perf_counter() - t0
name = func.__name__
arg_str = ','.join(repr(arg) for arg in args)
print('[%0.8fs] %s(%s) -> %r' %(elasped,name,arg_str,result))
return result
return clocked
@clock
def snooze(seconds):
time.sleep(seconds)
@clock
def factorial(n):
if n < 2:
return 1
else:
return n *factorial(n-1)
if __name__=='__main__':
dis(clock)
print('*' * 40,'calling snooze(.123)')
snooze(.123)
print('*' * 40,'calling factorial(6)')
print('6!=',factorial(6))
输出
D:\python3.9.7\python.exe D:/my-python-test/liuchangdepython/decorate.py
5 0 LOAD_CLOSURE 0 (func)
2 BUILD_TUPLE 1
4 LOAD_CONST 1 (<code object clocked at 0x0000025028D6B240, file "D:\my-python-test\liuchangdepython\decorate.py", line 5>)
6 LOAD_CONST 2 ('clock.<locals>.clocked')
8 MAKE_FUNCTION 8 (closure)
10 STORE_FAST 1 (clocked)
13 12 LOAD_FAST 1 (clocked)
14 RETURN_VALUE
Disassembly of <code object clocked at 0x0000025028D6B240, file "D:\my-python-test\liuchangdepython\decorate.py", line 5>:
6 0 LOAD_GLOBAL 0 (time)
2 LOAD_METHOD 1 (perf_counter)
4 CALL_METHOD 0
6 STORE_FAST 1 (t0)
7 8 LOAD_DEREF 0 (func)
10 LOAD_FAST 0 (args)
12 CALL_FUNCTION_EX 0
14 STORE_FAST 2 (result)
8 16 LOAD_GLOBAL 0 (time)
18 LOAD_METHOD 1 (perf_counter)
20 CALL_METHOD 0
22 LOAD_FAST 1 (t0)
24 BINARY_SUBTRACT
26 STORE_FAST 3 (elasped)
9 28 LOAD_DEREF 0 (func)
30 LOAD_ATTR 2 (__name__)
32 STORE_FAST 4 (name)
10 34 LOAD_CONST 1 (',')
36 LOAD_METHOD 3 (join)
38 LOAD_CONST 2 (<code object <genexpr> at 0x0000025028D6B190, file "D:\my-python-test\liuchangdepython\decorate.py", line 10>)
40 LOAD_CONST 3 ('clock.<locals>.clocked.<locals>.<genexpr>')
42 MAKE_FUNCTION 0
44 LOAD_FAST 0 (args)
46 GET_ITER
48 CALL_FUNCTION 1
50 CALL_METHOD 1
52 STORE_FAST 5 (arg_str)
11 54 LOAD_GLOBAL 4 (print)
56 LOAD_CONST 4 ('[%0.8fs] %s(%s) -> %r')
58 LOAD_FAST 3 (elasped)
60 LOAD_FAST 4 (name)
62 LOAD_FAST 5 (arg_str)
64 LOAD_FAST 2 (result)
66 BUILD_TUPLE 4
68 BINARY_MODULO
70 CALL_FUNCTION 1
72 POP_TOP
12 74 LOAD_FAST 2 (result)
76 RETURN_VALUE
Disassembly of <code object <genexpr> at 0x0000025028D6B190, file "D:\my-python-test\liuchangdepython\decorate.py", line 10>:
10 0 LOAD_FAST 0 (.0)
>> 2 FOR_ITER 14 (to 18)
4 STORE_FAST 1 (arg)
6 LOAD_GLOBAL 0 (repr)
8 LOAD_FAST 1 (arg)
10 CALL_FUNCTION 1
12 YIELD_VALUE
14 POP_TOP
16 JUMP_ABSOLUTE 2
>> 18 LOAD_CONST 0 (None)
20 RETURN_VALUE
**************************************** calling snooze(.123)
[0.12288130s] snooze(0.123) -> None
**************************************** calling factorial(6)
[0.00000080s] factorial(1) -> 1
[0.00001210s] factorial(2) -> 2
[0.00001910s] factorial(3) -> 6
[0.00002510s] factorial(4) -> 24
[0.00003130s] factorial(5) -> 120
[0.00003950s] factorial(6) -> 720
6!= 720
Process finished with exit code 0
代码解释
❶ 定义内部函数 clocked, 它接受任意个定位参数。
❷ 这行代码可用, 是因为 clocked 的闭包中包含自由变量 func。
❸ 返回内部函数, 取代被装饰的函数。 示例 7-16 演示了 clock 装饰器
的用法。
@clock
def factorial(n):
等价于
factorial = clock(factorial)
在示例中, factorial 会作为 func 参数传给 clock。 然后, clock 函数会返回 clocked 函数, Python 解释器在背后会把 clocked 赋值给 factorial。 其实,查看 factorial 的 name 属性, 会得到
如下结果:
print(factorial.__name__)
clocked
所以, 现在 factorial 保存的是 clocked 函数的引用。 自此之后, 每
次调用 factorial(n), 执行的都是 clocked(n)。 clocked 大致做了
下面几件事。
(1) 记录初始时间 t0。
(2) 调用原来的 factorial 函数, 保存结果。
(3) 计算经过的时间。
(4) 格式化收集的数据, 然后打印出来。
(5) 返回第 2 步保存的结果。
这是装饰器的典型行为: 把被装饰的函数替换成新函数, 二者接受相同
的参数, 而且(通常) 返回被装饰的函数本该返回的值, 同时还会做些
额外操作。