1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
import time
import functools
# 函数对象有一个__name__属性 可以拿到函数的名字
def now():
return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), time.localtime()
f = now
print(now.__name__)
print(f.__name__)
# 但是现在我们要增强now()函数的功能 比如需要在函数调用前后打印日志 但又不想改变函数的定义 这种在代码运行期间动态添加功能的方式 称之为decorator(装饰器)
# 本质上decorator就是一个可以返回函数的高阶函数。所以我们要定义一个可以打印日志的decorator的,可以定义如下:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
# 上面的log是一个decorator 所以接受一个函数作为参数 并返回一个函数。我们需要借助py的@语法,将decorator置于函数的定义处
@log
def now_new():
return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
print(now_new())
# 在调用now_new函数前 会先打印一行日志 再调用now_new函数 相当于执行了now_nwe = log(now_new)
# 由于log是一个decorator 返回一个函数 所以原来的now_new函数还在 只不过是现在的 now_new指向了一个新的函数 所以调用now_new将执行一个新函数 即log函数中返回的wrapper函数
# wrapper函数的参数定义是(*args, **kw)所以可以接收任意参数 在wrapper函数内部 先print输出 再调用now_new函数
# 如果decorator本身需要传入参数,那就需要编写一个返回decorator的函数,比如要自定义log的文本
print('-------------------')
def new_log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__) + '--------\r\n')
return func(*args, **kw)
return wrapper
return decorator
@new_log('execute')
def new_now():
return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
# print(new_now())
# 这个三层嵌套的效果相当于:
new_now = new_log('execute')(new_now)
print(new_now.__name__)
# 前面都没有问题 但是最后的new_now.__name__变了 因为返回的wrapper函数的__name__就是wrapper,我们需要吧原始的__name__等属性复制到wrapper函数中 否则一些依赖函数签名的函数代码就会执行出错
# 不需要编写wrapper.__name__ = func.__name__这样的代码 python内置的 functools.wraps就可以搞定 写法如下
def log(func):
# 这里的wraps我猜也算是一个装饰器吧
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
# practice:请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
def log_time(func):
@functools.wraps(func)
def wrapper(*args, **kw):
start_time = time.time()
res = func(*args, **kw)
end_time = time.time()
print('%s ran in %s seconds' % (func.__name__, (end_time - start_time) * 1000))
return res
return wrapper
@log_time
def test(x, y):
time.sleep(1)
return x + y
print(test(1, 2))
```
|