🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
~~~ wrapper ['ræpə] n.包装; ~~~ [如何理解Python装饰器? - 刘志军的回答 - 知乎 ](https://www.zhihu.com/question/26930016/answer/99243411) [详解Python的装饰器](https://www.cnblogs.com/cicaday/p/python-decorator.html) `decorator`:返回函数的高阶函数 参数是函数,返回是函数。 定义 ----- **作用**:为函数增加额外功能 **应用场景**:有切面需求的场景,如:插入日志、性能测试、事务处理、缓存、权限校验等 **好处**:可以抽离出大量与函数功能本身无关的雷同代码并继续重用(重用:装饰多个类同函数) #### 完整装饰器写法: 1、返回内函数(包裹原函数和装饰程序,内函数替代原函数) 2、内函数的返回值是原函数的执行结果 3、复制原函数内置属性到内函数(functools.wraps) **tips**: 1) *装饰器接口约束*:装饰器本身必须是callable对象(如函数,重载__call__的类),返回一个callable对象 - 返回callable对象并非指最终返回结果,而是指第一个return返回的内层包裹 - 个别情况不返回callable对象,如内置装饰器@property 3) 装饰器函数在原函数之前定义 ~~~ # @语法糖: # 原函数变量 = 执行:装饰器 # # foo = deco(foo) = wrapper # foo(...) = wrapper(...) = 原foo(...) import functools def deco(func): @functools.wraps(func) # 复制属性 def wrapper(*args, **kwargs): # wrapper可接收任意参数 pass # do something return func(*args, **kwargs) # func执行wrapper接收的任意参数 return wrapper @deco # foo = deco(foo) def foo(arg1, arg2): return arg1 + arg2 res = foo(1, 2) # 同:wrapper(1,2) 同:do something后执行原foo(1,2) print(foo.__name__) ~~~ @ --- 语法糖@作用等同如下: ~~~ # 该例子也是一个装饰器,只不过代码没有使用@优雅 # 显然该装饰器无法接受参数 def debug(func): def wrapper(): print(f'[DEBUG]: enter {format(func.__name__)}()') return func() return wrapper def say_hello(): print('hello!') say_hello = debug(say_hello) # 添加功能并保持原函数名不变 say_hello() # 输出: # [DEBUG]: enter say_hello() # hello! ~~~ 为什么返回内函数 --------------------- 直接返回原函数: 装饰代码仅在返回原函数时执行了一次,以后即不会再执行。 ~~~ def deco(func): print('hello') # decorator code:something else to do return func @deco def foo(arg1, arg2): return arg1 + arg2 print(foo(1,2)) print(foo(3,4)) ''' 输出: hello 3 7 ''' ~~~ functools.wraps ------------------- 使用`functools.wraps`复制原函数内置属性到内函数。 `wraps`本身也是`functools`模块的一个装饰器。 ~~~ # 不使用装饰器 def foo(): pass print(foo.__name__) # foo ~~~ ~~~ # 装饰器改动了原函数的某些属性 def deco(func): def wrapper(*args, **kwargs): pass # do something ... return func(*args, **kwargs) return wrapper @deco def foo(): pass print(foo.__name__) # wrapper ~~~