1- ** 装饰器:本质上是一个Python函数(嵌套函数),他可以让其他函数在不做任何代码变动的情况下增加额外的功能,装饰器的返回值也是一个函数对象。有了装饰器,可以抽离出大量与函数功能本身无关的代码并重复使用。需求场景如:插入日志、性能测试、权限校验等。**
1+ ** 装饰器:本质上是一个Python函数(嵌套函数),它的参数是另一个函数(被装饰函数) ,他可以让其他函数在不做任何代码变动的情况下增加额外的功能,装饰器的返回值也是一个函数对象。有了装饰器,可以抽离出大量与函数功能本身无关的代码并重复使用。需求场景如:插入日志、性能测试、权限校验等。**
22
3- #### 最简单的装饰器
3+ #### 1. 最简单的装饰器
44被装饰函数没有参数:
55
66``` python
@@ -20,7 +20,7 @@ if __name__ == '__main__':
2020 # func_a 耗时: 3.0008597373962402
2121 func_a()
2222```
23- #### 被装饰函数带参数
23+ #### 2. 被装饰函数带参数
2424可变参数+关键字参数可以表示所有的参数情况。
2525
2626``` python
@@ -42,7 +42,7 @@ if __name__ == '__main__':
4242 # func_a 耗时: 2.0003809928894043
4343 func_a(2 )
4444```
45- #### 带参数的装饰器
45+ #### 3. 带参数的装饰器
4646实现一个装饰器,控制被装饰函数的执行次数。三层嵌套。
4747
4848``` python
@@ -64,3 +64,185 @@ if __name__ == '__main__':
6464 # foo执行第3次
6565 foo()
6666```
67+
68+ #### 4.装饰器有一个关键的特性是,它们在被装饰的函数定义之后立即运行。这通常是在导入时(即 Python 加载模块时)
69+ ``` python
70+ registry = []
71+
72+
73+ def register (func ):
74+ print (' running register({} )' .format(func))
75+ registry.append(func)
76+ return func
77+
78+
79+ @register
80+ def f1 ():
81+ print (' running f1()' )
82+
83+
84+ def f2 ():
85+ print (' running f2()' )
86+
87+
88+ if __name__ == ' __main__' :
89+ print (registry)
90+ print (' running main' )
91+ f1()
92+ f2()
93+ ```
94+
95+ 运行结果
96+ ``` python
97+ running register(< function f1 at 0x 000002341B186620> )
98+ [< function f1 at 0x 000002341B186620> ]
99+ running main
100+ running f1()
101+ running f2()
102+ ```
103+
104+ #### 5.标准库中的装饰器 functools.lru_cache()
105+ functools.lru_cache是非常实用的装饰器,它实现了备忘功能。这是一项优化技术,它把耗时的函数的结果保存
106+ 起来,避免传入相同的参数时重复计算。
107+ * 参数maxsize:指定缓存的最大结果数量
108+
109+ ** 不使用functools.lru_cache的情况**
110+
111+ ``` python
112+ import time
113+
114+ def count_time (func ):
115+ def count (* args , ** kwargs ):
116+ start = time.time()
117+ res = func(* args, ** kwargs)
118+ end = time.time()
119+ print (' 执行函数{} ({} )耗时: {} ' .format(func.__name__ , args[0 ], end - start))
120+ return res
121+ return count
122+
123+ @count_time
124+ def fibonacci (n ):
125+ if n < 2 :
126+ return n
127+ return fibonacci(n- 1 ) + fibonacci(n- 1 )
128+
129+ if __name__ == ' __main__' :
130+ fibonacci(5 )
131+ ```
132+ 运行结果,发现同一个值计算了多次
133+ ``` python
134+ 执行函数fibonacci(1 )耗时: 0.0
135+ 执行函数fibonacci(1 )耗时: 0.0
136+ 执行函数fibonacci(2 )耗时: 0.0
137+ 执行函数fibonacci(1 )耗时: 0.0
138+ 执行函数fibonacci(1 )耗时: 0.0
139+ 执行函数fibonacci(2 )耗时: 0.0
140+ 执行函数fibonacci(3 )耗时: 0.0
141+ 执行函数fibonacci(1 )耗时: 0.0
142+ 执行函数fibonacci(1 )耗时: 0.0
143+ 执行函数fibonacci(2 )耗时: 0.0
144+ 执行函数fibonacci(1 )耗时: 0.0
145+ 执行函数fibonacci(1 )耗时: 0.0
146+ 执行函数fibonacci(2 )耗时: 0.0
147+ 执行函数fibonacci(3 )耗时: 0.0
148+ 执行函数fibonacci(4 )耗时: 0.0
149+ 执行函数fibonacci(1 )耗时: 0.0
150+ 执行函数fibonacci(1 )耗时: 0.0
151+ 执行函数fibonacci(2 )耗时: 0.0
152+ 执行函数fibonacci(1 )耗时: 0.0
153+ 执行函数fibonacci(1 )耗时: 0.0
154+ 执行函数fibonacci(2 )耗时: 0.0
155+ 执行函数fibonacci(3 )耗时: 0.0
156+ 执行函数fibonacci(1 )耗时: 0.0
157+ 执行函数fibonacci(1 )耗时: 0.0
158+ 执行函数fibonacci(2 )耗时: 0.0
159+ 执行函数fibonacci(1 )耗时: 0.0
160+ 执行函数fibonacci(1 )耗时: 0.0
161+ 执行函数fibonacci(2 )耗时: 0.0
162+ 执行函数fibonacci(3 )耗时: 0.0
163+ 执行函数fibonacci(4 )耗时: 0.0
164+ 执行函数fibonacci(5 )耗时: 0.0
165+ ```
166+
167+ ** 使用functools.lru_cache的情况**
168+ ``` python
169+ import time
170+ import functools
171+
172+ def count_time (func ):
173+ def count (* args , ** kwargs ):
174+ start = time.time()
175+ res = func(* args, ** kwargs)
176+ end = time.time()
177+ print (' 执行函数{} ({} )耗时: {} ' .format(func.__name__ , args[0 ], end - start))
178+ return res
179+ return count
180+
181+ @functools.lru_cache ()
182+ @count_time
183+ def fibonacci (n ):
184+ if n < 2 :
185+ return n
186+ return fibonacci(n- 1 ) + fibonacci(n- 1 )
187+
188+ if __name__ == ' __main__' :
189+ fibonacci(5 )
190+ ```
191+
192+ 结果,避免了重复计算
193+ ``` python
194+ 执行函数fibonacci(1 )耗时: 0.0
195+ 执行函数fibonacci(2 )耗时: 0.0
196+ 执行函数fibonacci(3 )耗时: 0.0
197+ 执行函数fibonacci(4 )耗时: 0.0
198+ 执行函数fibonacci(5 )耗时: 0.0
199+ ```
200+
201+ #### 6.标准库中的装饰器 functools.singledispatch
202+
203+ Python3.4新增的 functools.singledispatch 装饰器可以把整体方案拆分成多个模块,甚至可以为你无法修改的类提供专门的函数。使用@singledispatch 装饰的普通函数会变成泛函数(generic function):根据第一个参数的类型,以不同方式执行相同操作的一组函数.
204+
205+ ``` python
206+ import functools
207+ import html
208+ import numbers
209+ from collections import abc
210+
211+
212+ @functools.singledispatch
213+ def htmlize (obj ):
214+ content = html.escape(repr (obj))
215+ return ' <pre>{} </pre>' .format(content)
216+
217+
218+ @htmlize.register (str )
219+ def _ (text ):
220+ content = html.escape(text).replace(' \n ' , ' </br>\n ' )
221+ return ' <pre>{} </pre>' .format(content)
222+
223+
224+ @htmlize.register (numbers.Integral)
225+ def _ (n ):
226+ return ' <pre>{} ({} )</pre>' .format(n, hex (n))
227+
228+
229+ @htmlize.register (tuple )
230+ @htmlize.register (abc.MutableSequence)
231+ def _ (seq ):
232+ inner = ' </li>\n <li>' .join(htmlize(item) for item in seq)
233+ return ' <ul>\n <li>' + inner + ' </li>\n </ul>'
234+
235+ if __name__ == ' __main__' :
236+ print (htmlize(17 ))
237+ print (htmlize([' alpha' , 66 , {3 , 2 , 1 }]))
238+ ```
239+
240+ 结果
241+ ``` python
242+ < pre> 17 (0x 11 )< / pre>
243+ < ul>
244+ < li>< pre> alpha< / pre>< / li>
245+ < li>< pre> 66 (0x 42 )< / pre>< / li>
246+ < li>< pre> {1 , 2 , 3 }< / pre>< / li>
247+ < / ul>
248+ ```
0 commit comments