Python装饰器详解
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """
4 __----~~~~~~~~~~~------___
5 . . ~~//====...... __--~ ~~
6 -. \_|// |||\\ ~~~~~~::::... /~
7 ___-==_ _-~o~ \/ ||| \\ _/~~-
8 __---~~~.==~||\=_ -_--~/_-~|- |\\ \\ _/~
9 _-~~ .=~ | \\-_ '-~7 /- / || \ /
10 .~ .~ | \\ -_ / /- / || \ /
11 / ____ / | \\ ~-_/ /|- _/ .|| \ /
12 |~~ ~~|--~~~~--_ \ ~==-/ | \~--===~~ .\
13 ' ~-| /| |-~\~~ __--~~
14 |-~~-_/ | | ~\_ _-~ /\
15 / \ \__ \/~ \__
16 _--~ _/ | .-~~____--~-/ ~~==.
17 ((->/~ '.|||' -_| ~~-/ , . _||
18 -_ ~\ ~~---l__i__i__i--~~_/
19 _-~-__ ~) \--______________--~~
20 //.-~~~-~_--~- |-------~~~~~~~~
21 //.-~~~--\
22 神兽保佑
23 代码无BUG!
24
25 """
26
27 # import heartrate
28 #
29 # heartrate.trace(browser=True)
30
31 # 01. 最简单的装饰器
32 '''
33 def decorator(func):
34 def wrapper(*args, **kw):
35 return func()
36 return wrapper
37
38 @decorator
39 def function():
40 print("hello, decorator")
41
42
43 function()
44 '''
45
46 # 02. 日志打印器
47 '''
48 def decorator(func):
49 def wrapper(*args, **kwargs):
50 print('开始准备执行:{}函数了'.format(func.__name__))
51
52 # 真正的执行函数
53 func(*args, **kwargs)
54
55 print('{}函数执行完毕'.format(func.__name__))
56
57 return wrapper
58
59
60 @decorator
61 def add(x, y):
62 print('{} + {} = {}'.format(x, y, x + y))
63
64
65 add(5, 7)
66 '''
67
68 # 03. 时间计时器
69 '''
70 import time
71
72
73 def timer(func):
74 def wrapper(*args, **kw):
75 t1 = time.time()
76 # 这是函数真正执行的地方
77 func(*args, **kw)
78 t2 = time.time()
79
80 # 计算下时长
81 cost_time = t2 - t1
82 print("花费时间:{}秒".format(cost_time))
83
84 return wrapper
85
86
87 @timer
88 def want_sleep(sleep_time):
89 time.sleep(sleep_time)
90
91
92 want_sleep(10)
93 '''
94
95 # 04. 带参数的函数装饰器 (注意,是装饰器带参数,装饰器修饰的函数不带参数)
96 # 在原有函数装饰器的基础上外面再套一层函数
97 '''
98 def say_hello(contry):
99 def wrapper(func):
100 def deco(*args, **kwargs):
101 if contry == "china":
102 print("你好!")
103 elif contry == "america":
104 print('hello.')
105 else:
106 return
107
108 # 真正执行函数的地方
109 func(*args, **kwargs)
110
111 return deco
112
113 return wrapper
114
115
116 # 小明,中国人
117 @say_hello("china")
118 def xiaoming():
119 pass
120
121
122 # jack,美国人
123 @say_hello("america")
124 def jack():
125 pass
126
127
128 xiaoming()
129 print("------------")
130 jack()
131 '''
132
133 # 05. 高阶:不带参数的类装饰器 (装饰器修饰的函数带参数)
134 """
135 基于类装饰器的实现,必须实现 __call__ 和 __init__两个内置函数。
136 __init__ :接收被装饰函数
137 __call__ :实现装饰逻辑。
138 """
139
140 """
141 class logger():
142 def __init__(self, func):
143 self.func = func
144
145 def __call__(self, *args, **kwargs):
146 print("[INFO]: the function {func}() is running..." \
147 .format(func=self.func.__name__))
148 return self.func(*args, **kwargs)
149
150
151 @logger
152 def say(something):
153 print("say {}!".format(something))
154
155
156 say("hello")
157 """
158
159 # 06. 高阶:带参数的类装饰器 (注意:类装饰器带参数,装饰的函数也带参数)
160
161 """
162 带参数和不带参数的类装饰器有很大的不同。
163 __init__ :不再接收被装饰函数,而是接收传入参数。
164 __call__ :接收被装饰函数,实现装饰逻辑。
165 """
166
167 """
168 class logger():
169 def __init__(self, level='INFO'):
170 self.level = level
171
172 def __call__(self, func): # 接受函数
173 def wrapper(*args, **kwargs):
174 print("[{level}]: the function {func}() is running..." \
175 .format(level=self.level, func=func.__name__))
176 func(*args, **kwargs)
177
178 return wrapper # 返回函数
179
180
181 @logger(level='WARNING')
182 def say(something):
183 print("say {}!".format(something))
184
185
186 say("hello")
187 """
188
189 # 07. 使用偏函数与类实现装饰器
190
191 """
192 绝大多数装饰器都是基于函数和闭包实现的,但这并非制造装饰器的唯一方式。
193
194 事实上,Python 对某个对象是否能通过装饰器( @decorator)形式使用只有一个要求:decorator 必须是一个“可被调用(callable)的对象。
195
196 对于这个 callable 对象,我们最熟悉的就是函数了。
197
198 除函数之外,类也可以是 callable 对象,只要实现了__call__ 函数(上面几个例子已经接触过了)。
199
200 还有容易被人忽略的偏函数其实也是 callable 对象。
201
202 接下来就来说说,如何使用类和偏函数结合实现一个与众不同的装饰器。
203
204 如下所示,DelayFunc 是一个实现了 __call__ 的类,delay 返回一个偏函数,在这里 delay 就可以做为一个装饰器。
205 """
206
207 """
208 import time
209 import functools
210
211
212 class DelayFunc:
213 def __init__(self, duration, func):
214 self.duration = duration
215 self.func = func
216
217 def __call__(self, *args, **kwargs):
218 print(f'Wait for {self.duration} seconds...')
219 time.sleep(self.duration)
220 return self.func(*args, **kwargs)
221
222 def eager_call(self, *args, **kwargs):
223 print('Call without delay')
224 return self.func(*args, **kwargs)
225
226
227 def delay(duration):
228
229 # 装饰器:推迟某个函数的执行。
230 # 同时提供 .eager_call 方法立即执行
231 # 此处为了避免定义额外函数,
232 # 直接使用 functools.partial 帮助构造 DelayFunc 实例
233 return functools.partial(DelayFunc, duration)
234
235
236 @delay(duration=2)
237 def add(a, b):
238 print(a+b)
239 return a + b
240
241 add(3,5)
242 """
243
244 """
245 >>> add # 可见 add 变成了 Delay 的实例
246 <__main__.DelayFunc object at 0x107bd0be0>
247 >>>
248 >>> add(3,5) # 直接调用实例,进入 __call__
249 Wait for 2 seconds...
250 8
251 >>>
252 >>> add.func # 实现实例方法
253 <function add at 0x107bef1e0>
254 """
255
256 # 08. 装饰类的装饰器
257 instances = {}
258
259
260 def singleton(cls):
261 def get_instance(*args, **kw):
262 # print(cls) # <class '__main__.User'>
263 cls_name = cls.__name__ # User
264 print('===== 1 ====')
265 # print(instances) # {}
266 if not cls_name in instances:
267 print('===== 2 ====')
268 instance = cls(*args, **kw)
269 instances[cls_name] = instance
270 return instances[cls_name]
271
272 return get_instance
273
274
275 @singleton
276 class User:
277 _instance = None
278
279 def __init__(self, name):
280 print('===== 3 ====')
281 self.name = name
282
283
284 u1 = User("Tom")
285 """
286 ===== 1 ====
287 ===== 2 ====
288 ===== 3 ====
289 """
290
291 u1.age = 20
292
293 u2 = User("Lily")
294 """
295 ===== 1 ====
296 """
297
298 print(u2.age) # 20
299
300 print(u1 == u2) # True
301
302
303 # 09. wraps 装饰器
304 def wrapper(func):
305 print(func.__name__) # wrapped
306
307 def inner_function():
308 pass
309
310 return inner_function
311
312
313 @wrapper
314 def wrapped():
315 print('3')
316
317
318 print(wrapped.__name__) # inner_function
319 print(wrapper(wrapped).__name__) # inner_function
320 '''
321 上边执行func 和下边 decorator(func) 是等价的,所以上面 func.__name__ 是等价于下面decorator(func).__name__ 的
322 '''
323
324 # 使用 functools .wraps 装饰器,将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper)
325 from functools import wraps
326
327
328 def wrapper(func):
329 print(func.__name__) # wrapped
330
331 @wraps(func)
332 def inner_function():
333 pass
334
335 return inner_function
336
337
338 @wrapper
339 def wrapped():
340 pass
341
342
343 print(wrapped.__name__) # wrapped
344
345 """
346 准确点说,wraps 其实是一个偏函数对象(partial),源码如下
347
348 def wraps(wrapped,
349 assigned = WRAPPER_ASSIGNMENTS,
350 updated = WRAPPER_UPDATES):
351 return partial(update_wrapper, wrapped=wrapped,
352 assigned=assigned, updated=updated)
353 """
354
355 """
356 可以看到wraps其实就是调用了一个函数update_wrapper,改写上面的代码,在不使用 wraps的情况下,也可以让 wrapped.__name__ 打印出 wrapped
357 """
358 from functools import update_wrapper
359
360 WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
361 '__annotations__')
362
363
364 def wrapper(func):
365 def inner_function():
366 pass
367
368 update_wrapper(inner_function, func, assigned=WRAPPER_ASSIGNMENTS)
369 return inner_function
370
371
372 @wrapper
373 def wrapped():
374 pass
375
376
377 print('---')
378 print(wrapped.__name__)
379
380
381 # 10. 内置装饰器:property
382 # 通常存在于类中,可以将一个函数定义成一个属性,属性的值就是该函数return的内容,同时,会将这个函数变成另外一个装饰器
383 class Student(object):
384 def __init__(self, name):
385 self.name = name
386 self.name = None
387
388 @property
389 def age(self):
390 return self._age
391
392 @age.setter
393 def age(self, value):
394 if not isinstance(value, int):
395 raise ValueError('输入不合法:年龄必须为数值!')
396 if not 0 < value < 100:
397 raise ValueError('输入不合法:年龄范围必须0-100')
398 self._age = value
399
400 @age.deleter
401 def age(self):
402 del self._age
403
404
405 xiaoming = Student("小明")
406
407 # 设置属性
408 xiaoming.age = 25
409
410 # 查询属性
411 print(xiaoming.age)
412
413 # 删除属性
414 del xiaoming.age
415
416 # 11. 其他装饰器:装饰器实战
417 import signal
418
419
420 class TimeoutException(Exception):
421 def __init__(self, error='Timeout waiting for response from Cloud'):
422 Exception.__init__(self, error)
423
424
425 def timeout_limit(timeout_time):
426 def wraps(func):
427 def handler(signum, frame):
428 raise TimeoutException()
429
430 def deco(*args, **kwargs):
431 signal.signal(signal.SIGALRM, handler)
432 signal.alarm(timeout_time)
433 func(*args, **kwargs)
434 signal.alarm(0)
435
436 return deco
437
438 return wraps