装饰器 Decorator 可以在不修改函数本体的情况下,为函数添加额外功能。例如,在授权认证的时候,如果没有 Decorator,那么我们可能需要手动写一个函数 auth() 然后在每次目标函数执行之前,先调用一次 auth()、处理异常……然后再执行函数本体。这无疑是非常麻烦的。有了 Decorator 之后,我们可以把 auth() 的调用、异常处理放在 Decorator 中,这样只需要在函数外面写上 @auth 即可检查授权。这样做既可以保持函数内部含义清晰,Decorator 的代码也可以复用。
Decorator 运行逻辑
当 Python 检查到有函数 func() 被装饰器 @dec 装饰后,Python 会在函数被执行前 ,运行 decorator 的定义代码,用户定义的 func() 实际替换为 wrapper() 的函数体,但是函数名仍然保留. 用户调用的时候还是 func(),但是每次调用还会运行定义在 wrapper() 里内容.
下面这个例子可以帮助我们更好地理解 Decorator 的机制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def decorator (some ): print (f"in decorator(some={some} ), before decorator(func)" ) def decorator (func ): print ("in decorator(func), before wrapper()" ) def wrapper (*args, **kwargs ): print ("in wrapper(), before calling func()" ) result = func(*args, **kwargs) print ("in wrapper(), after calling func()" ) return result print ("in decorator(func), after wrapper()" ) return wrapper print (f"in decorator(some={some} ), after decorator(func)" ) return decorator @decorator(some="$$$" ) def myfunc (): print ("in myfunc()" ) myfunc() myfunc()
把两行 myfunc() 注释前后观察输出. 即使没有真正调用 myfunc(),decorator 的代码仍然会运行;注释掉两行的 myfunc() 后,decorator 的四个 print() 也只运行了一次,说明并不是每次调用 myfunc() 才展开 decorator 的,而 in wrapper() 的输出随着 myfunc() 调用次数的增多也在增多
定义 Decorator
Decorator 的本质是一个函数,其返回值也是一个函数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def decorator (function ): def wrapper (*args, **kwargs ): result = function(*args, **kwargs) return result return wrapper @decorator def my_function (): print ()
Decorator 自己也可以带参数,方法是在定义时多套一层 def,并且第二层同名
1 2 3 4 5 6 7 8 9 10 11 12 13 def decorator_with_arguments (some_args ): def decorator_with_arguments (function ): def wrapper (*args, **kwargs ): result = function(*args, **kwargs) return result return wrapper return decorator_with_arguments @decorator_with_arguments("some args" ) def my_function (): print ("my_function" )
Decorator 保留原函数的 DocString
Decorator 实际返回的函数对象是 wrapper() 而非 func(),因此,decorator 装饰后会把 func() 的 docstring、函数名之类的统统调包为 wrapper(). 想要保留的话,需要使用用 @functools.wraps(func) 来装饰 wrapper()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import functoolsdef decorator_with_arguments (some_args ): def decorator_with_arguments (function ): @functools.wraps(function ) def wrapper (*args, **kwargs ): result = function(*args, **kwargs) return result return wrapper return decorator_with_arguments @decorator_with_arguments("some args" ) def my_function (): print ("my_function" )