Python-装饰器

1、无参装饰器:

  举例1:

1 def add(x, y):
2     return x + y
3 需求后:
4 def add(x, y):
5     print('{}.{}'.format(x, y))
6     return x + y

    需求:一个加法函数添加一些功能,如果向上面那样,但是上面所示是一种硬编码,也就是说,每次都会出现打印,而不是在需要的时候添加。耦合度太高!

  ***举例2:装饰器的引入:

  1 NO 1
  2 def add(x, y):
  3     return x + y
  4 
  5 def add(x, y):
  6     print('{}.{}'.format(x, y))
  7     return x + y
  8 
  9 
 10 NO 2 将 要修饰的内容剥离出来,被修饰的函数分开,在修饰函数里调用被修饰的函数,这样,就不会修改业务函数
 11 def add(x, y):
 12     return  x + y
 13 
 14 def sub(x, y):
 15     return  x - y
 16 
 17 def logger(fn, x, y):
 18     print('function {}, x-{}, y-{}'.format(fn.__name__, x, y))
 19     ret = fn(4, 5)
 20     return ret
 21 
 22 print('result = {}'.format(logger(add, 4, 5)))
 23 print('result = {}'.format(logger(sub, 4, 5)))
 24 
 25 
 26 function add, x-4, y-5
 27 result = 9
 28 function sub, x-4, y-5
 29 result = -1
 30 
 31 
 32 
 33 
 34 
 35 NO 3 # 可以传递各种参数
 36 
 37 def add(x, y):
 38     return  x + y
 39 
 40 def sub(x, y):
 41     return  x - y
 42 
 43 def a(x, y):
 44     pass
 45 
 46 def b(m, n, *args, y, x, **kwargs):
 47     pass
 48 
 49 
 50 def logger(fn, *args, **kwargs): # 这里是形参定义,在这里只是对实参的中转站,不作处理
 51     print('function {}, x-{}, y-{}'.format(fn.__name__, 4, 5))
 52     ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
 53     return ret
 54 
 55 
 56 print('result = {}'.format(logger(add, 4, 5)))
 57 print('result = {}'.format(logger(b, 3,4,5,6,x = 7,y = 8)))
 58 
 59 
 60 function add, x-4, y-5
 61 result = 9
 62 function b, x-4, y-5
 63 result = None
 64 
 65 
 66 NO 4 将上述 修饰函数柯里化
 67 
 68 def add(x, y):
 69     return  x + y
 70 
 71 def sub(x, y):
 72     return  x - y
 73 
 74 def a(x, y):
 75     pass
 76 
 77 def b(m, n, *args, y, x, **kwargs):
 78     pass
 79 
 80 
 81 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
 82     def inner(*args, **kwargs):
 83         print('function {}, x-{}, y-{}'.format(fn.__name__, 4, 5))
 84         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
 85         return ret
 86     return inner
 87 
 88 # print(logger(add)(4,5))
 89 
 90 add = logger(add) #在add 覆盖之前,已经将add对应的函数赋值给fn这个辨识符
 91 
 92 print(add(4,5)) # 此时就像调用add() 函数本身!!
 93 
 94 
 95 NO 5 装饰器上场   @函数名
 96 
 97 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
 98     def inner(*args, **kwargs):
 99         print('function {}, x-{}, y-{}'.format(fn.__name__, 4, 5))
100         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
101         return ret
102     return inner
103 
104 @logger # add = logger(add)  ----> add = inner
105 def add(x, y):
106     return  x + y
107 
108 print(add(4, 5)) # 这的add就是inner

 

    这种装饰器:

      • 它是一个函数
      • 函数作为他的形参
      • 返回值也是一个函数
      • 可以使用@函数名方式,简化
        • 此处总结不准确,只是方便理解

    装饰器 和 高阶函数:

        装饰器是高阶函数,但装饰器是对传入的函数功能的装饰(功能增强)

    举例3: 函数功能增强

 1 #  NO 1 装饰器增强:增加一个时间计时器
 2 
 3 import datetime
 4 
 5 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
 6     def inner(*args, **kwargs):
 7         print('执行业务功能之前')
 8         start = datetime.datetime.now()
 9 
10         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
11 
12         delta = (start - datetime.datetime.now()).total_seconds()
13 
14         print('{}-{}'.format(fn.__name__,delta))
15         print('执行业务功能之后')
16         
17         return ret
18     return inner
19 
20 @logger # add = logger(add)  ----> add = inner
21 def add(x, y):
22     return  x + y
23 
24 
25 print(add(3,4))

 

 

2、有参装饰器:

  1 # #有参装饰器
  2 
  3 # no 1 文档字符串
  4 import datetime
  5 
  6 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
  7     def wrapper(*args, **kwargs):
  8         ''' this is wrapper function'''
  9 
 10         print('执行业务功能之前')
 11         start = datetime.datetime.now()
 12 
 13         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
 14 
 15         delta = (start - datetime.datetime.now()).total_seconds()
 16 
 17         print('{}-{}'.format(fn.__name__,delta))
 18         print('执行业务功能之后')
 19 
 20         return ret
 21     return wrapper
 22 
 23 @logger # add = logger(add)  ----> add = inner
 24 def add(x, y):
 25     ''' this is add function'''
 26     return  x + y
 27 
 28 print(add.__name__) #打印的是 wrapper的
 29 print(add.__doc__)
 30 
 31 # wrapper
 32 #  this is wrapper function
 33 
 34 
 35 # # no 2    输出文旦字符串  print(help(add))   当作帮助信息,让别人看懂
 36 import datetime
 37 
 38 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
 39     def wrapper(*args, **kwargs):
 40         ''' this is wrapper function'''
 41 
 42         print('执行业务功能之前')
 43         start = datetime.datetime.now()
 44 
 45         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
 46 
 47         delta = (start - datetime.datetime.now()).total_seconds()
 48 
 49         print('{}-{}'.format(fn.__name__,delta))
 50         print('执行业务功能之后')
 51 
 52         return ret
 53     return wrapper
 54 
 55 @logger # add = logger(add)  ----> add = inner
 56 def add(x, y):
 57     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
 58     return  x + y
 59 
 60 print(add.__name__)
 61 print(add.__doc__)
 62 print('-------------------')
 63 print(help(add))   # 帮助文档,即文档字符串的信息
 64 
 65 
 66 # wrapper
 67 #  this is wrapper function
 68 # -------------------
 69 # Help on function wrapper in module __main__:
 70 #
 71 # wrapper(*args, **kwargs)
 72 #     this is wrapper function
 73 #
 74 # None
 75 
 76 
 77 # no 3 有参装饰器的引入 ,上面的结果,可以看出,自己一定是被装饰过了,自己的信息都变了,别人打开帮助信息并不是自己想看到的
 78 
 79 import datetime
 80 
 81 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
 82 
 83     def wrapper(*args, **kwargs):
 84         ''' this is wrapper function'''
 85         print('执行业务功能之前')
 86         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
 87         print('执行业务功能之后')
 88         return ret
 89 
 90     wrapper.__name__ = fn.__name__ #
 91     wrapper.__doc__ = fn.__doc__ #
 92     return wrapper
 93 
 94 @logger # add = logger(add)  ----> add = wrapper
 95 def add(x, y):
 96     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
 97     return  x + y
 98 
 99 print(add.__name__)
100 print(add.__doc__)
101 
102 # NO 4 变形2
103 
104 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
105     def copy_properties(src, dest):
106         dest.__name__ = src.__name__ #
107         dest.__doc__ = src.__doc__ #
108 
109     def wrapper(*args, **kwargs):
110         ''' this is wrapper function'''
111         print('执行业务功能之前')
112         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
113         print('执行业务功能之后')
114         return ret
115     copy_properties(fn, wrapper)
116     # wrapper.__name__ = fn.__name__ #
117     # wrapper.__doc__ = fn.__doc__ #
118     return wrapper
119 
120 @logger # add = logger(add)  ----> add = wrapper
121 def add(x, y):
122     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
123     return  x + y
124 
125 print(add.__name__)
126 print(add.__doc__)
127 
128 # NO 5 变现3  将修改函数放全局中,并将其柯里化
129 
130 def copy_properties(src):
131     def _copy(dest):
132         dest.__name__ = src.__name__ #
133         dest.__doc__ = src.__doc__ #
134     return _copy
135 
136 
137 def logger(fn):
138 
139 
140     def wrapper(*args, **kwargs):
141         ''' this is wrapper function'''
142         print('执行业务功能之前')
143         ret = fn(*args, **kwargs)
144         print('执行业务功能之后')
145         return ret
146 
147     copy_properties(fn)( wrapper)  # 因为copy_properties() 被柯里化了
148     # wrapper.__name__ = fn.__name__ #
149     # wrapper.__doc__ = fn.__doc__ #
150     return wrapper
151 
152 @logger # add = logger(add)  ----> add = wrapper
153 def add(x, y):
154     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
155     return  x + y
156 
157 print(add.__name__)
158 print(add.__doc__)
159 
160 
161 
162 # NO 6 使用装饰器将其改造,相当于说,给wrapper()函数增加一个装饰器使其能够实现上面的功能
163 # # 也就是说修改 wrapper的文档字符串,改为业务函数的文档字符串
164 def copy_properties(src): # 要理解柯里化的调用方式
165     def _copy(dest):
166         dest.__name__ = src.__name__ # src 和dest 都是函数名
167         dest.__doc__ = src.__doc__ #
168         return dest
169     return _copy
170 
171 def logger(fn):
172     #    @copy_properties   # wrapper=copy_properties(wrapper) wrapper=_copy  此时执行_copy()是缺少参数的,也就是说,这样的不对的
173     @copy_properties(fn)  #  # 等价于这句话 copy_properties(fn)( wrapper) ,上面的版本的调用方式
174     # wrapper=copy_properties(fn)(wrapper)  ---> wrapper = _copy --->_copy(wrapper)---->因为此时的wrapper已经变为_copy,所以还要将其变为原来的,当然是修改了字符串文档后的,所以_copy()返回dest,即wrapper
175     def wrapper(*args, **kwargs):
176         ''' this is wrapper function'''
177         print('执行业务功能之前')
178         ret = fn(*args, **kwargs)
179         print('执行业务功能之后')
180         return ret
181     return wrapper
182 
183 @logger # add = logger(add)  ----> add = wrapper
184 def add(x, y):
185     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
186     return  x + y
187 
188 print(add.__name__)
189 print(add.__doc__)
190 
191 
192 # NO7 上面只是练习,事实上,是有模块的,处理字符串文档的,使用  update_wrapper(w, fn)
193 from functools import update_wrapper
194 
195 def logger(fn):
196     def w(*args, **kwargs):
197         ''' this is wrapper function'''
198         print('执行业务功能之前')
199         ret = fn(*args, **kwargs)
200         print('执行业务功能之后')
201         return ret
202     update_wrapper(w, fn) # 参数前者是修饰,后者是被修饰,它会return w,所以下面不用return w
203     # return w
204 
205 @logger # add = logger(add)  ----> add = wrapper
206 def add(x, y):
207     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
208     return  x + y
209 
210 print(add.__name__)
211 print(add.__doc__)
212 
213 # NO 8 类似update_wrapper的柯里化wraps 偏函数
214 from functools import update_wrapper,wraps
215 
216 def logger(fn):
217     @wraps(fn) # w=wraps(fn)(w)结构就是一个柯里化的样式
218     def w(*args, **kwargs):
219         ''' this is wrapper function'''
220         print('执行业务功能之前')
221         ret = fn(*args, **kwargs)
222         print('执行业务功能之后')
223         return ret
224     # update_wrapper(w, fn)
225 
226     return w
227 
228 @logger # add = logger(add)  ----> add = wrapper
229 def add(x, y):
230     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
231     return  x + y
232 
233 print(add.__name__)
234 print(add.__doc__)
235 print(add.__dict__)
236 print(add.__module__)
237 print(add.__qualname__)
238 print(add.__annotations__)
239 
240 # add
241 #  this is add function
242 # {'__wrapped__': <function add at 0x0000000001EC3378>}
243 # __main__
244 # add
245 # {}
246 
247 '''
248 WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
249                        '__annotations__')
250 WRAPPER_UPDATES = ('__dict__',)
251 def update_wrapper(wrapper,
252                    wrapped,
253                    assigned = WRAPPER_ASSIGNMENTS,
254                    updated = WRAPPER_UPDATES):
255     """Update a wrapper function to look like the wrapped function
256 
257        wrapper is the function to be updated
258        wrapped is the original function
259        assigned is a tuple naming the attributes assigned directly
260        from the wrapped function to the wrapper function (defaults to
261        functools.WRAPPER_ASSIGNMENTS)
262        updated is a tuple naming the attributes of the wrapper that
263        are updated with the corresponding attribute from the wrapped
264        function (defaults to functools.WRAPPER_UPDATES)
265     """
266     for attr in assigned:
267         try:
268             value = getattr(wrapped, attr) # 拿属性 ,这里的wrapped 就是 add
269         except AttributeError:
270             pass
271         else:
272             setattr(wrapper, attr, value) # 设置属性,wrapper
273     for attr in updated:
274         getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
275     # Issue #17482: set __wrapped__ last so we don't inadvertently copy it
276     # from the wrapped function when updating __dict__
277     wrapper.__wrapped__ = wrapped  # 在w 函数上加了一个新的属性 fn
278     # Return the wrapper so this can be used as a decorator via partial()
279     return wrapper # 这个很重要,就是w = w,没有就出现大问题
280 
281 def wraps(wrapped,  ---------------这是一个偏函数 # 就剩wrapper 没有给定
282           assigned = WRAPPER_ASSIGNMENTS,
283           updated = WRAPPER_UPDATES):
284     """Decorator factory to apply update_wrapper() to a wrapper function
285 
286        Returns a decorator that invokes update_wrapper() with the decorated
287        function as the wrapper argument and the arguments to wraps() as the
288        remaining arguments. Default arguments are as for update_wrapper().
289        This is a convenience function to simplify applying partial() to
290        update_wrapper().
291     """
292     return partial(update_wrapper, wrapped=wrapped,
293                    assigned=assigned, updated=updated)
294 
295 '''
296 
297 # NO 9 增强装饰器
298 import datetime
299 import  time
300 from functools import update_wrapper,wraps
301 
302 def logger(fn, m=5):
303     @wraps(fn) # w=wraps(fn)(w)结构就是一个柯里化的样式
304     def w(*args, **kwargs):
305         ''' this is wrapper function'''
306         start = datetime.datetime.now()
307         ret = fn(*args, **kwargs)
308         delta = (start - datetime.datetime.now()).total_seconds()
309         if delta < m:
310             print("++++++")
311         else:
312             print('------')
313         return ret
314     return w
315 
316 @logger # add = logger(add)  ----> add = wrapper
317 def add(x, y):
318     ''' this is add function'''  # 这个只能放在第一行才能打印!!!!!
319     time.sleep(7)
320     return  x + y
321 
322 print(add(3,4))
323 
324 # NO 10 上面的函数,参数m 必须是缺省值,所以不灵活,所以继续修改,柯梨花
325 import datetime
326 import  time
327 from functools import update_wrapper,wraps
328 
329 def logger(m): # 这样的话,可以随便传入更多的参数,一般最多三层,因为最终要回到_logger上
330     def _logger(fn):
331         @wraps(fn) # w=wraps(fn)(w)结构就是一个柯里化的样式
332         def w(*args, **kwargs):
333             ''' this is wrapper function'''
334             start = datetime.datetime.now()
335             ret = fn(*args, **kwargs)
336             delta = (start - datetime.datetime.now()).total_seconds()
337             if delta < m:
338                 print("++++++")
339             else:
340                 print('------')
341             return ret
342         return w
343     return _logger
344 
345 
346 @logger(3) # add = logger(3)(add)  ----> add = wrapper
347 def add(x, y):
348     ''' this is add function'''  # 这个只能放在第一行才能打印!!!!!
349     time.sleep(2)
350     return  x + y
351 
352 print(add(4,5))
353 
354 # no 11,将内部的一下功能提取出来,比如:写入日志,否则得修改原码
355 import datetime
356 import  time
357 from functools import update_wrapper,wraps
358 
359 def logger(m,
360            func=lambda name, took:print('function {} took {}s'.format(name, took))):# 加入打印到屏幕上
361     def _logger(fn):
362         @wraps(fn) # w=wraps(fn)(w)结构就是一个柯里化的样式
363         def w(*args, **kwargs):
364             ''' this is wrapper function'''
365             start = datetime.datetime.now()
366             ret = fn(*args, **kwargs)
367             delta = (start - datetime.datetime.now()).total_seconds()
368             if delta < m:
369                 func(fn.__name__, delta) # 将这里的功能提取出来,放到最外面,作为缺省参数
370             else:
371                 print('------')
372             return ret
373         return w
374     return _logger
375 
376 
377 @logger(3) # add = logger(3)(add)  ----> add = wrapper
378 def add(x, y):
379     ''' this is add function'''  # 这个只能放在第一行才能打印!!!!!
380     time.sleep(2)
381     return  x + y
382 
383 print(add(4,5))
384 
385 
386 
387 # no 12  如果缺省参数表示的函数不能一行搞定,也可以写最外面,然后调用
388 import datetime
389 import  time
390 from functools import update_wrapper,wraps
391 
392 def toLog(name, took):# 加入写到日志中
393     pass
394 
395 
396 def logger(m,func=toLog):# 这里写成缺省,如果要写到其他地方,可以在@looger()中添加位置参数,覆盖这个缺省参数
397     def _logger(fn):
398         @wraps(fn) # w=wraps(fn)(w)结构就是一个柯里化的样式
399         def w(*args, **kwargs):
400             ''' this is wrapper function'''
401             start = datetime.datetime.now()
402             ret = fn(*args, **kwargs)
403             delta = (start - datetime.datetime.now()).total_seconds()
404             if delta < m:
405                 func(fn.__name__, delta) # 将这里的功能提取出来,放到最外面,作为缺省参数
406             else:
407                 print('------')
408             return ret
409         return w
410     return _logger
411 
412 
413 @logger(3) # add = logger(3)(add)  ----> add = wrapper
414 def add(x, y):
415     ''' this is add function'''  # 这个只能放在第一行才能打印!!!!!
416     time.sleep(2)
417     return  x + y
418 
419 print(add(4,5))

     注意

 1 from functools import wraps
 2 
 3 def logger(fn):
 4     print('------------')
 5     @wraps(fn)
 6     def wrapper(*args, **kwargs):
 7         print(add.__annotations__, '---')
 8         set = fn(*args, **kwargs)
 9         return  set
10     print("+++++++++++++")
11     return wrapper
12 
13 @logger # add = logger(add) = wrapper
14 def add(x:int, y:int) -> int:# 返回值的注解
15     return  x + y
16 
17 @logger # sub = logger(sub) = wrapper
18 def sub(x, y):
19     return x - y
20 '''
21 直接运行,可以看到是执行了的,一分析便可以看出,虽然add,sub没有调用,但是
22 @ 语法糖是要调用logger的,所以函数会执行。
23 其次,当执行add(3, 4) 时,wrapper的字符串文档会被add的覆盖,在执行sub(3, 4)
24 的时候,同样,wrapper的字符串文档也会被sub的覆盖,但是两者没有影响,是因为,logger
25 是一个全局函数,而wrapper是一个嵌套函数,也就是说是一个局部变量,所以执行add 和sub
26 的时候,在不同的栈里,全局函数是一个,先压栈,执行add 的时候,压wrapper,执行完后,弹出,在压
27 sub对应的wrapper,所以是互不影响的。
28 线程调用函数,线程跟栈有关,对创建对象(堆是散放的),栈是对象的引用'''
29 # ------------
30 # +++++++++++++
31 # ------------
32 # +++++++++++++

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

posted @ 2018-08-27 23:14  JerryZao  阅读(278)  评论(1编辑  收藏  举报