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