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

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

1、无参装饰器:

  举例1:

1 def add(x, y):2     return x + y3 需求后: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 ret102     return inner103 104 @logger # add = logger(add)  ----> add = inner105 def add(x, y):106     return  x + y107 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 ret18     return inner19 20 @logger # add = logger(add)  ----> add = inner21 def add(x, y):22     return  x + y23 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 变形2103 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 ret115     copy_properties(fn, wrapper)116     # wrapper.__name__ = fn.__name__ #117     # wrapper.__doc__ = fn.__doc__ #118     return wrapper119 120 @logger # add = logger(add)  ----> add = wrapper121 def add(x, y):122     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!123     return  x + y124 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 _copy135 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 ret146 147     copy_properties(fn)( wrapper)  # 因为copy_properties() 被柯里化了148     # wrapper.__name__ = fn.__name__ #149     # wrapper.__doc__ = fn.__doc__ #150     return wrapper151 152 @logger # add = logger(add)  ----> add = wrapper153 def add(x, y):154     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!155     return  x + y156 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 dest169     return _copy170 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,即wrapper175     def wrapper(*args, **kwargs):176         ''' this is wrapper function'''177         print('执行业务功能之前')178         ret = fn(*args, **kwargs)179         print('执行业务功能之后')180         return ret181     return wrapper182 183 @logger # add = logger(add)  ----> add = wrapper184 def add(x, y):185     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!186     return  x + y187 188 print(add.__name__)189 print(add.__doc__)190 191 192 # NO7 上面只是练习,事实上,是有模块的,处理字符串文档的,使用  update_wrapper(w, fn)193 from functools import update_wrapper194 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 ret202     update_wrapper(w, fn) # 参数前者是修饰,后者是被修饰,它会return w,所以下面不用return w203     # return w204 205 @logger # add = logger(add)  ----> add = wrapper206 def add(x, y):207     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!208     return  x + y209 210 print(add.__name__)211 print(add.__doc__)212 213 # NO 8 类似update_wrapper的柯里化wraps 偏函数214 from functools import update_wrapper,wraps215 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 ret224     # update_wrapper(w, fn)225 226     return w227 228 @logger # add = logger(add)  ----> add = wrapper229 def add(x, y):230     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!231     return  x + y232 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 # add241 #  this is add function242 # {'__wrapped__': 
}243 # __main__244 # add245 # {}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 function256 257 wrapper is the function to be updated258 wrapped is the original function259 assigned is a tuple naming the attributes assigned directly260 from the wrapped function to the wrapper function (defaults to261 functools.WRAPPER_ASSIGNMENTS)262 updated is a tuple naming the attributes of the wrapper that263 are updated with the corresponding attribute from the wrapped264 function (defaults to functools.WRAPPER_UPDATES)265 """266 for attr in assigned:267 try:268 value = getattr(wrapped, attr) # 拿属性 ,这里的wrapped 就是 add269 except AttributeError:270 pass271 else:272 setattr(wrapper, attr, value) # 设置属性,wrapper273 for attr in updated:274 getattr(wrapper, attr).update(getattr(wrapped, attr, {}))275 # Issue #17482: set __wrapped__ last so we don't inadvertently copy it276 # from the wrapped function when updating __dict__277 wrapper.__wrapped__ = wrapped # 在w 函数上加了一个新的属性 fn278 # 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 function285 286 Returns a decorator that invokes update_wrapper() with the decorated287 function as the wrapper argument and the arguments to wraps() as the288 remaining arguments. Default arguments are as for update_wrapper().289 This is a convenience function to simplify applying partial() to290 update_wrapper().291 """292 return partial(update_wrapper, wrapped=wrapped,293 assigned=assigned, updated=updated)294 295 '''296 297 # NO 9 增强装饰器298 import datetime299 import time300 from functools import update_wrapper,wraps301 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 ret314 return w315 316 @logger # add = logger(add) ----> add = wrapper317 def add(x, y):318 ''' this is add function''' # 这个只能放在第一行才能打印!!!!!319 time.sleep(7)320 return x + y321 322 print(add(3,4))323 324 # NO 10 上面的函数,参数m 必须是缺省值,所以不灵活,所以继续修改,柯梨花325 import datetime326 import time327 from functools import update_wrapper,wraps328 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 ret342 return w343 return _logger344 345 346 @logger(3) # add = logger(3)(add) ----> add = wrapper347 def add(x, y):348 ''' this is add function''' # 这个只能放在第一行才能打印!!!!!349 time.sleep(2)350 return x + y351 352 print(add(4,5))353 354 # no 11,将内部的一下功能提取出来,比如:写入日志,否则得修改原码355 import datetime356 import time357 from functools import update_wrapper,wraps358 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 ret373 return w374 return _logger375 376 377 @logger(3) # add = logger(3)(add) ----> add = wrapper378 def add(x, y):379 ''' this is add function''' # 这个只能放在第一行才能打印!!!!!380 time.sleep(2)381 return x + y382 383 print(add(4,5))384 385 386 387 # no 12 如果缺省参数表示的函数不能一行搞定,也可以写最外面,然后调用388 import datetime389 import time390 from functools import update_wrapper,wraps391 392 def toLog(name, took):# 加入写到日志中393 pass394 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 ret409 return w410 return _logger411 412 413 @logger(3) # add = logger(3)(add) ----> add = wrapper414 def add(x, y):415 ''' this is add function''' # 这个只能放在第一行才能打印!!!!!416 time.sleep(2)417 return x + y418 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  set10     print("+++++++++++++")11     return wrapper12 13 @logger # add = logger(add) = wrapper14 def add(x:int, y:int) -> int:# 返回值的注解15     return  x + y16 17 @logger # sub = logger(sub) = wrapper18 def sub(x, y):19     return x - y20 '''21 直接运行,可以看到是执行了的,一分析便可以看出,虽然add,sub没有调用,但是22 @ 语法糖是要调用logger的,所以函数会执行。23 其次,当执行add(3, 4) 时,wrapper的字符串文档会被add的覆盖,在执行sub(3, 4)24 的时候,同样,wrapper的字符串文档也会被sub的覆盖,但是两者没有影响,是因为,logger25 是一个全局函数,而wrapper是一个嵌套函数,也就是说是一个局部变量,所以执行add 和sub26 的时候,在不同的栈里,全局函数是一个,先压栈,执行add 的时候,压wrapper,执行完后,弹出,在压27 sub对应的wrapper,所以是互不影响的。28 线程调用函数,线程跟栈有关,对创建对象(堆是散放的),栈是对象的引用'''29 # ------------30 # +++++++++++++31 # ------------32 # +++++++++++++

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

转载于:https://www.cnblogs.com/JerryZao/p/9545444.html

你可能感兴趣的文章
PAT (Basic Level) Practise - 换个格式输出整数
查看>>
bzoj1901:Zju2112 Dynamic Rankings
查看>>
POJ 2104 K-th Number
查看>>
[BZOJ1083] [SCOI2005] 繁忙的都市 (kruskal)
查看>>
Django之Auth模块 实现登录,退出,自带session 与认证功能的一个重要的模块
查看>>
【机器学习_10】十大基础算法:分类_逻辑回归
查看>>
A+B Format
查看>>
SVN
查看>>
LeetCode - N-Queens II
查看>>
sublime3+quick智能提示
查看>>
UIDevice的简易说明
查看>>
Smarty模板引擎技术(一)
查看>>
nodejs中&#x5B89;&#x5353;&#x7AEF;的编码如何转换为中文
查看>>
实验四
查看>>
js实现全屏
查看>>
Javascript中最常用的61个经典技巧
查看>>
进程,线程通信与同步
查看>>
我看APT攻防对抗(2):APT攻击的案例
查看>>
Two Sum - Unique pairs Lintcode
查看>>
存储过程的优点
查看>>