函数装饰器的工作原理,假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示:
1 #funA 作为装饰器函数 2 def funA(fn): 3 #... 4 fn() # 执行传入的fn参数 5 #... 6 return ‘...‘ 7 8 @funA 9 def funB(): 10 #...
实际上,上面程序完全等价于下面的程序:
1 def funA(fn): 2 #... 3 fn() # 执行传入的fn参数 4 #... 5 return ‘...‘ 6 7 def funB(): 8 #... 9 10 funB = funA(funB)
通过比对以上 2 段程序不难发现,使用函数装饰器 A() 去装饰另一个函数 B(),其底层执行了如下 2 步操作:
举个实例:
1 #funA 作为装饰器函数 2 def funA(fn): 3 print("努力学习") 4 fn() # 执行传入的fn参数 5 print("123") 6 return "装饰器函数的返回值" 7 8 @funA 9 def funB(): 10 print("学习 Python")
程序执行流程为:
努力学习
学习 Python
123
在此基础上,如果在程序末尾添加如下语句:
1 print(funB)
其输出结果为:
装饰器函数的返回值
显然,被“@函数”修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值),即如果装饰器函数的返回值为普通变量,那么被修饰的函数名就变成了变量名;同样,如果装饰器返回的是一个函数的名称,那么被修饰的函数名依然表示一个函数。
实际上,所谓函数装饰器,就是通过装饰器函数,在不修改原函数的前提下,来对函数的功能进行合理的扩充。
在分析 funA() 函数装饰器和 funB() 函数的关系时,细心的人可能会发现一个问题,即当 funB() 函数无参数时,可以直接将 funB 作为 funA() 的参数传入。但是,如果被修饰的函数本身带有参数,那应该如何传值呢?
比较简单的解决方法就是在函数装饰器中嵌套一个函数,该函数带有的参数个数和被装饰器修饰的函数相同。例如:
1 def funA(fn): 2 # 定义一个嵌套函数 3 def say(arc): 4 print("Python:",arc) 5 return say 6 7 @funA 8 def funB(arc): 9 print("funB():", a) 10 funB("努力学习")
程序执行结果为:
Python:努力学习
分析一下这个程序,其实,它和如下程序是等价的:
1 def funA(fn): 2 # 定义一个嵌套函数 3 def say(arc): 4 print("Python:",arc) 5 return say 6 7 def funB(arc): 8 print("funB():", a) 9 10 funB = funA(funB) 11 funB("学习")
如果运行此程序会发现,它的输出结果和上面程序相同。
显然,通过 funB() 函数被装饰器 funA() 修饰,funB 就被赋值为 say。这意味着,虽然我们在程序显式调用的是 funB() 函数,但其实执行的是装饰器嵌套的 say() 函数。
但还有一个问题需要解决,即如果当前程序中,有多个(≥ 2)函数被同一个装饰器函数修饰,这些函数带有的参数个数并不相等,怎么办呢?
最简单的解决方式是用 *args 和 **kwargs 作为装饰器内部嵌套函数的参数,*args 和 **kwargs 表示接受任意数量和类型的参数。举个例子:
1 def funA(fn): 2 # 定义一个嵌套函数 3 def say(*args,**kwargs): 4 fn(*args,**kwargs) 5 return say 6 7 @funA 8 def funB(arc): 9 print("python:",arc) 10 11 @funA 12 def other_funB(name,arc): 13 print(name,arc) 14 funB("学习") 15 other_funB("Python:","学习")
运行结果为:
Python:学习
原文:https://www.cnblogs.com/Outsider07/p/14591794.html