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

 

posted @ 2019-09-05 15:05  哈喽哈喽111111  阅读(644)  评论(0编辑  收藏  举报