1- ** 装饰器:本质上是一个Python函数(嵌套函数),它的参数是另一个函数(被装饰函数),他可以让其他函数在不做任何代码变动的情况下增加额外的功能,装饰器的返回值也是一个函数对象。有了装饰器,可以抽离出大量与函数功能本身无关的代码并重复使用。需求场景如:插入日志、性能测试、权限校验等。**
2-
3- #### 1.最简单的装饰器
4- 被装饰函数没有参数:
5-
6- ``` python
7- import time
8- def count_time (func ):
9- def deco ():
10- start = time.time()
11- func()
12- end = time.time()
13- print (' {} 耗时: {} ' .format(func.__name__ , end- start))
14- return deco
15- @count_time
16- def func_a ():
17- time.sleep(3 )
18-
19- if __name__ == ' __main__' :
20- # func_a 耗时: 3.0008597373962402
21- func_a()
22- ```
23- #### 2.被装饰函数带参数
24- 可变参数+关键字参数可以表示所有的参数情况。
25-
26- ``` python
27- import time
28- def count_time (func ):
29- def deco (* args , ** kwargs ):
30- start = time.time()
31- func(* args, ** kwargs)
32- end = time.time()
33- print (' {} 耗时: {} ' .format(func.__name__ , end- start))
34- return deco
35- @count_time
36- def func_a (t ):
37- time.sleep(t)
38-
39- if __name__ == ' __main__' :
40- # func_a 耗时: 1.0008749961853027
41- func_a(1 )
42- # func_a 耗时: 2.0003809928894043
43- func_a(2 )
44- ```
45- #### 3.带参数的装饰器
46- 实现一个装饰器,控制被装饰函数的执行次数。三层嵌套。
47-
48- ``` python
49- def repate (num = 3 ):
50- def actual_deco (func ):
51- def deco (* args , ** kwargs ):
52- for i in range (num):
53- print (' {} 执行第{} 次' .format(func.__name__ , i+ 1 ))
54- func()
55- return deco
56- return actual_deco
57- @repate (num = 3 )
58- def foo ():
59- pass
60-
61- if __name__ == ' __main__' :
62- # foo执行第1次
63- # foo执行第2次
64- # foo执行第3次
65- foo()
66- ```
67-
68- #### 4.装饰器有一个关键的特性是,它们在被装饰的函数定义之后立即运行。这通常是在导入时(即 Python 加载模块时)
1+ # 装饰器
2+ ## 1.装饰器定义
3+ ** 装饰器的本质是一个闭包(嵌套函数中内函数引用了外函数的局部变量,外函数的返回包含对内函数的引用,这样就形成了一个闭包)。[ 变量的作用域与闭包] ( 变量的作用域.md ) **
4+
5+ * 所以装饰器也可以理解为一个嵌套函数,它的参数是被装饰的函数,返回值也是一个函数对象
6+ * 装饰器可以在原函数代码没有任何变动的基础上,增加额外的功能
7+ * 装饰器的应用场景有:性能测试(计算函数运行时间),插入日志,权限校验(Django的@login_required),缓存(缓存函数的计算结果,避免重复计算,提高效率)
8+
9+ ## 2.装饰器的实现
10+ #### 2.1. 最简单的装饰器
11+ 计算函数运行时间
6912``` python
70- registry = []
13+ import time
14+
15+ def count_time (func ):
16+ def wrap ():
17+ start = time.time()
18+ func()
19+ end = time.time()
20+ print (' 函数名:{} 耗时:{} ' .format(func.__name__ , end - start))
21+ return wrap
22+
23+ @count_time
24+ def func ():
25+ print (' func正在运行...' )
26+ time.sleep(3 )
27+ print (' func运行结束' )
28+
29+ if __name__ == ' __main__' :
30+ func()
31+ ```
32+ ``` python
33+ func正在运行...
34+ func运行结束
35+ 函数名:func 耗时:3.0006725788116455
36+ ```
37+
38+ #### 2.2. 被装饰的函数带参数
39+ 可变参数+关键字参数可以表示所有的参数情况
40+ ``` python
41+ import time
42+
43+ def count_time (func ):
44+ def wrap (* args , ** kwargs ):
45+ start = time.time()
46+ func(* args, ** kwargs)
47+ end = time.time()
48+ print (' 函数名:{} 耗时:{} ' .format(func.__name__ , end - start))
49+ return wrap
50+
51+ @count_time
52+ def func (seconds ):
53+ print (' func正在运行...' )
54+ time.sleep(seconds)
55+ print (' func运行结束' )
56+
57+ if __name__ == ' __main__' :
58+ func(3 )
59+ func(5 )
60+ ```
61+
62+ #### 2.3. 装饰器带参数
63+ 定义一个装饰器,控制被装饰函数运行次数
64+ ``` python
65+ def run (times ):
66+ def deco (func ):
67+ def wrap (* args , ** kwargs ):
68+ for _ in range (times):
69+ func(* args, ** kwargs)
70+ return wrap
71+ return deco
72+
73+ @run (times = 3 )
74+ def func ():
75+ print (' func正在运行...' )
76+ print (' func运行结束' )
77+
78+ if __name__ == ' __main__' :
79+ func()
80+ ```
81+ ``` python
82+ func正在运行...
83+ func运行结束
84+ func正在运行...
85+ func运行结束
86+ func正在运行...
87+ func运行结束
88+ ```
89+
90+ #### 2.4. 装饰器用类实现(无参数)
91+ CountTime()相当于调用CountTime类的\_\_ call__ ()方法
92+
93+ ``` python
94+ import time
95+
96+ class CountTime :
97+ def __call__ (self , func ):
98+ def wrap (* args , ** kwargs ):
99+ start = time.time()
100+ func(* args, ** kwargs)
101+ end = time.time()
102+ print (' 函数名:{} 耗时:{} ' .format(func.__name__ , end - start))
103+ return wrap
104+
105+ @CountTime ()
106+ def func ():
107+ print (' func正在运行...' )
108+ time.sleep(3 )
109+ print (' func运行结束' )
110+
111+ if __name__ == ' __main__' :
112+ func()
113+ ```
114+ ``` python
115+ func正在运行...
116+ func运行结束
117+ 函数名:func 耗时:3.0221292972564697
118+ ```
119+
120+ #### 2.5. 装饰器用类实现(有参数)
121+ 通过init初始化方法传参
122+ ``` python
123+ class Run :
124+ def __init__ (self , times ):
125+ self .times = times
126+
127+ def __call__ (self , func ):
128+ def wrap (* args , ** kwargs ):
129+ for _ in range (self .times):
130+ func(* args, ** kwargs)
131+ return wrap
132+
133+ @Run (times = 3 )
134+ def func ():
135+ print (' func正在运行...' )
136+ print (' func运行结束' )
137+
138+ if __name__ == ' __main__' :
139+ func()
140+ ```
141+ ``` python
142+ func正在运行...
143+ func运行结束
144+ func正在运行...
145+ func运行结束
146+ func正在运行...
147+ func运行结束
148+ ```
149+
150+ ## 3.functools.wraps
151+ 以上装饰器存在一个小问题,被装饰函数虽然并没有改动,但是被装饰函数的元信息不见了,如下:
152+ * 被装饰函数的函数名是func,但是经过装饰器装饰后,函数名变为了wrap
153+
154+ ``` python
155+ import time
156+
157+ def count_time (func ):
158+ def wrap ():
159+ start = time.time()
160+ func()
161+ end = time.time()
162+ print (' 函数名:{} 耗时:{} ' .format(func.__name__ , end - start))
163+ return wrap
164+
165+ @count_time
166+ def func ():
167+ print (' func正在运行...' )
168+ time.sleep(3 )
169+ print (' func运行结束' )
71170
171+ if __name__ == ' __main__' :
172+ print (func.__name__ ) # wrap
173+ ```
174+
175+ ** 通过functools.wraps来避免这种问题**
176+
177+ ``` python
178+ import functools
179+ import time
180+
181+ def count_time (func ):
182+ @functools.wraps (func)
183+ def wrap ():
184+ start = time.time()
185+ func()
186+ end = time.time()
187+ print (' 函数名:{} 耗时:{} ' .format(func.__name__ , end - start))
188+ return wrap
189+
190+ @count_time
191+ def func ():
192+ print (' func正在运行...' )
193+ time.sleep(3 )
194+ print (' func运行结束' )
195+
196+ if __name__ == ' __main__' :
197+ print (func.__name__ ) # func
198+ ```
199+ ## 4.装饰器有一个关键的特性是,它们在被装饰的函数定义之后立即运行。这通常是在导入时(即 Python 加载模块时)
200+ ``` python
201+ registry = []
72202
73203def register (func ):
74204 print (' running register({} )' .format(func))
75205 registry.append(func)
76206 return func
77207
78-
79208@register
80209def f1 ():
81210 print (' running f1()' )
82211
83-
84212def f2 ():
85213 print (' running f2()' )
86214
87-
88215if __name__ == ' __main__' :
89216 print (registry)
90217 print (' running main' )
@@ -101,7 +228,7 @@ running f1()
101228running f2()
102229```
103230
104- #### 5.标准库中的装饰器 functools.lru_cache()
231+ ## 5.标准库中的装饰器 functools.lru_cache()
105232functools.lru_cache是非常实用的装饰器,它实现了备忘功能。这是一项优化技术,它把耗时的函数的结果保存
106233起来,避免传入相同的参数时重复计算。
107234* 参数maxsize:指定缓存的最大结果数量
@@ -198,7 +325,7 @@ if __name__ == '__main__':
198325执行函数fibonacci(5 )耗时: 0.0
199326```
200327
201- #### 6.标准库中的装饰器 functools.singledispatch
328+ ## 6.标准库中的装饰器 functools.singledispatch
202329
203330Python3.4新增的 functools.singledispatch 装饰器可以把整体方案拆分成多个模块,甚至可以为你无法修改的类提供专门的函数。使用@singledispatch 装饰的普通函数会变成泛函数(generic function):根据第一个参数的类型,以不同方式执行相同操作的一组函数.
204331
0 commit comments