博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python入门篇-装饰器
阅读量:6998 次
发布时间:2019-06-27

本文共 10817 字,大约阅读时间需要 36 分钟。

              Python入门篇-装饰器

                                      作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

 

 

一.装饰器概述

装饰器(无参)  它是一个函数  函数作为它的形参  返回值也是一个函数  可以使用@functionname方式,简化调用装饰器和高阶函数  装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)带参装饰器  它是一个函数  函数作为它的形参  返回值是一个不带参的装饰器函数  使用@functionname(参数列表)方式调用  可以看做在装饰器外层又加了一层函数

 

二.为什么要用装饰器

1>.在不是用装饰器的情况下,给某个函数添加功能

在解释为什么使用装饰器之前,完美来看一个需求:   一个加法函数,想增强它的功能,能够输出被调用过以及调用的参数信息    def add(x, y):      return x + y   增加信息输出功能:    def add(x, y):      print("call add, x + y") # 日志输出到控制台      return x + y   上面的加法函数是完成了需求,但是有以下的缺点    打印语句的耦合太高,换句话说,我们不推荐去修改初始的add函数原始代码。    加法函数属于业务功能,而输出信息的功能,属于非业务功能代码,不该放在业务函数加法中

2>.使用高阶函数给某个函数添加功能

1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6  7  8 def add(x,y): 9     return x + y10 11 def logger(func):12     print('begin') # 增强的输出13     f = func(4,5)14     print('end') # 增强的功能15     return f16 17 print(logger(add))18 19 20 21 #以上代码输出结果如下:22 begin23 end24 9

3>.解决了传参的问题,进一步改变

1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6  7  8 def add(x,y): 9     return x + y10 11 def logger(func,*args,**kwargs):12     print('begin') # 增强的输出13     f = func(*args,**kwargs)14     print('end') # 增强的功能15     return f16 17 print(logger(add,5,y=60))18 19 20 21 #以上代码输出结果如下:22 begin23 end24 65

4>.柯里化实现add函数功能增强

1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6  7 def add(x,y): 8     return x + y 9 10 def logger(fn):11     def wrapper(*args,**kwargs):12         print('begin')13         x = fn(*args,**kwargs)14         print('end')15         return x16     return wrapper17 18 # print(logger(add)(5,y=50))        #海航代码等价于下面两行代码,只是换了一种写法而已19 add = logger(add)20 print(add(x=5, y=10))21 22 23 #以上代码输出结果如下:24 begin25 end26 15

5>.装饰器语法糖

#!/usr/bin/env python#_*_coding:utf-8_*_#@author :yinzhengjie#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/#EMAIL:y1053419035@qq.com"""    定义一个装饰器"""def logger(fn):    def wrapper(*args,**kwargs):        print('begin')        x = fn(*args,**kwargs)        print('end')        return x    return wrapper@logger # 等价于add = logger(add),这就是装饰器语法def add(x,y):    return x + yprint(add(45,40))#以上代码输出结果如下:beginend85

 

三.帮助文档之文档字符串

1>.定义python的文档字符串

1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6  7  8 """ 9 Python的文档10     Python是文档字符串Documentation Strings11     在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号12     惯例是首字母大写,第一行写概述,空一行,第三行写详细描述13     可以使用特殊属性__doc__访问这个文档14 """15 16 def add(x,y):17     """This is a function of addition"""18     a = x+y19     return x + y20 21 print("name = {}\ndoc = {}".format(add.__name__, add.__doc__))22 23 print(help(add))24 25 26 27 #以上代码执行结果如下:28 name = add29 doc = This is a function of addition30 Help on function add in module __main__:31 32 add(x, y)33     This is a function of addition34 35 None

2>.装饰器的副作用

1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6  7  8 def logger(fn): 9     def wrapper(*args,**kwargs):10         'I am wrapper'11         print('begin')12         x = fn(*args,**kwargs)13         print('end')14         return x15     return wrapper16 17 @logger #add = logger(add)18 def add(x,y):19     '''This is a function for add'''20     return x + y21 22 23 print("name = {}\ndoc= {}".format(add.__name__, add.__doc__))      #原函数对象的属性都被替换了,而使用装饰器,我们的需求是查看被封装函数的属性,如何解决?24 25 26 27 28 #以上代码执行结果如下:29 name = wrapper30 doc= I am wrapper

3>.提供一个函数,被封装函数属性==copy==> 包装函数属性

1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6  7  8 """ 9     通过copy_properties函数将被包装函数的属性覆盖掉包装函数10     凡是被装饰的函数都需要复制这些属性,这个函数很通用11     可以将复制属性的函数构建成装饰器函数,带参装饰器12 """13 def copy_properties(src, dst): # 可以改造成装饰器14     dst.__name__ = src.__name__15     dst.__doc__ = src.__doc__16 17 def logger(fn):18     def wrapper(*args,**kwargs):19         'I am wrapper'20         print('begin')21         x = fn(*args,**kwargs)22         print('end')23         return x24     copy_properties(fn, wrapper)25     return wrapper26 27 @logger #add = logger(add)28 def add(x,y):29     '''This is a function for add'''30     return x + y31 32 print("name = {}\ndoc = {}".format(add.__name__, add.__doc__))33 34 35 36 37 #以上代码执行结果如下:38 name = add39 doc = This is a function for add

4>.提供一个函数,被封装函数属性==copy==> 包装函数属性,改造成带参装饰器

1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6  7  8 def copy_properties(src): # 柯里化 9     def _copy(dst):10         dst.__name__ = src.__name__11         dst.__doc__ = src.__doc__12         return dst13     return _copy14 15 def logger(fn):16     @copy_properties(fn) # wrapper = copy_properties(fn)(wrapper)17     def wrapper(*args,**kwargs):18         'I am wrapper'19         print('begin')20         x = fn(*args,**kwargs)21         print('end')22         return x23     return wrapper24 25 @logger #add = logger(add)26 def add(x,y):27     '''This is a function for add'''28     return x + y29 30 print("name = {}\ndoc = {}".format(add.__name__, add.__doc__))31 32 33 34 #以上代码执行结果如下:35 name = add36 doc = This is a function for add

 

四.装饰器案例

1>.无参装饰器

1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6  7 import datetime 8 import time 9 10 """11     定义一个装饰器12 """13 def logger(fn):14     def wrap(*args, **kwargs):15         # before 功能增强16         print("args={}, kwargs={}".format(args,kwargs))17         start = datetime.datetime.now()18         ret = fn(*args, **kwargs)19         # after 功能增强20         duration = datetime.datetime.now() - start21         print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))22         return ret23     return wrap24 25 @logger # 相当于add = logger(add),调用装饰器26 def add(x, y):27     print("===call add===========")28     time.sleep(2)29     return x + y30 31 print(add(4, y=7))32 33 34 35 #以上代码输出结果如下:36 args=(4,), kwargs={
'y': 7}37 ===call add===========38 function add took 2.000114s.39 11

2>.有参装饰器

1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6  7 import datetime,time 8  9 def copy_properties(src): # 柯里化10     def _copy(dst):11         dst.__name__ = src.__name__12         dst.__doc__ = src.__doc__13         return dst14     return _copy15 16 """17 定义装饰器:18     获取函数的执行时长,对时长超过阈值的函数记录一下19 """20 def logger(duration):21     def _logger(fn):22         @copy_properties(fn) # wrapper = wrapper(fn)(wrapper)23         def wrapper(*args,**kwargs):24             start = datetime.datetime.now()25             ret = fn(*args,**kwargs)26             delta = (datetime.datetime.now() - start).total_seconds()27             print('so slow') if delta > duration else print('so fast')28             return ret29         return wrapper30     return _logger31 32 @logger(5) # add = logger(5)(add)33 def add(x,y):34     time.sleep(3)35     return x + y36 37 print(add(5, 6))38 39 40 41 #以上代码执行结果如下:42 so fast43 11
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6  7 import datetime,time 8  9 def copy_properties(src): # 柯里化10     def _copy(dst):11         dst.__name__ = src.__name__12         dst.__doc__ = src.__doc__13         return dst14     return _copy15 16 """17 定义装饰器:18     获取函数的执行时长,对时长超过阈值的函数记录一下19 """20 def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):21     def _logger(fn):22         @copy_properties(fn) # wrapper = wrapper(fn)(wrapper)23         def wrapper(*args,**kwargs):24             start = datetime.datetime.now()25             ret = fn(*args,**kwargs)26             delta = (datetime.datetime.now() - start).total_seconds()27             if delta > duration:28                 func(fn.__name__, duration)29             return ret30         return wrapper31     return _logger32 33 @logger(5) # add = logger(5)(add)34 def add(x,y):35     time.sleep(3)36     return x + y37 38 print(add(5, 6))39 40 41 42 #以上代码输出结果如下:43 11
将记录的功能提取出来,这样就可以通过外部提供的函数来灵活的控制输出

 

五.functools模块

1>.functools概述

functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)    类似copy_properties功能  wrapper 包装函数、被更新者,wrapped 被包装函数、数据源  元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性'__module__', '__name__', '__qualname__', '__doc__', '__annotations__'模块名、名称、限定名、文档、参数注解  元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典  增加一个__wrapped__属性,保留着wrapped函数

2>.functools模块案例

1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6  7 import datetime, time, functools 8  9 def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):10     def _logger(fn):11         @functools.wraps(fn)12         def wrapper(*args,**kwargs):13             start = datetime.datetime.now()14             ret = fn(*args,**kwargs)15             delta = (datetime.datetime.now() - start).total_seconds()16             if delta > duration:17                 func(fn.__name__, duration)18             return ret19         return wrapper20     return _logger21 22 @logger(5) # add = logger(5)(add)23 def add(x,y):24     time.sleep(1)25     return x + y26 27 print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep='\n')28 29 30 31 32 #以上代码执行结果如下:33 1134 add35 
36 {
'__wrapped__':
}

 

转载于:https://www.cnblogs.com/yinzhengjie/p/10964821.html

你可能感兴趣的文章