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 # +++++++++++++