前置知识:
面向切面编程:
以一个例子来理解面向切面编程,假如程序写好了之后,现在发现要针对所有业务操作添加一个日志,或者在前面加一道权限,传统的做法是,改造每个业务方法,在里面假如所需要的功能,但是这样势必会把代码弄乱,而且以后再扩展还是更乱,面向切面编程的思想是将这部分功能插入进去,具体的实现例子就是python的@,也就是python的装饰器
闭包:
维基百科中的解释:在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
代码理解:
def print_msg():
tmp_message="this is a temporary message"
def printer():
print(tmp_message)
return printer
return_function=print_msg()
return_function()
输出:
this is a temporary message
在这个例子中,tmp_message是一个局部变量,在执行了print_msg()后就不应该继续存在了。但是嵌套函数引用了这个变量,将这个变量封装在了嵌套函数中,这样就形成了一个闭包,其中自由变量是tmp_message,函数是printer(),tem_message和printer()两者形成的闭包保存了执行时的上下文,可以脱离原本的作用域独立存在。
对应与python中,修饰器下面的函数就可以理解为一个闭包。
python装饰器的工作原理:
假设用funA函数装饰器去装饰funB函数,如下所示:
def funA(func):
func()
return
@funA
def funB():
print("This is funcB")
return
输出:
This is funcB
上述代码的执行过程完全等价于:
def funA(func):
func()
return
def funB():
print("This is funcB")
return
funB=funA(funB)
通过以上两段程序可以看出,使用函数装饰器A去装饰另一个函数B,其底层执行了如下两步操作:
- 将B作为参数传递给A函数
- 将A函数执行完成后的返回值反馈给B(funB=funA(funB))
再通过一个例子说明:
def funA(func):
print("first test")
func()
print("second test")
return "the return of funA"
@funA
def funB():
print("This is funB")
print(funB)
输出:
first test
This is funB
second test
the return of funA
由以上可以看出,被装饰器(@函数)修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值),即如果装饰器函数的返回值为普通变量,那么被修饰的函数名就变成了变量名,同样,如果装饰器返回的是一个函数的名称,那么被修饰的函数名依然表示一个函数。
实际上,函数装饰器的核心就是通过装饰器函数,在不修改原函数的前提下,来对函数的功能进行合理的扩充。
带参数的函数装饰器:
当B函数没有参数的时候,可以直接将B作为A()的参数传入,当被修饰的函数本身带有参数的时候,可以有以下处理方式:
比较简单的解决方法是在函数装饰器中嵌套一个函数,该函数带有的参数个数和被装饰器修饰的函数相同:
def funA(func):
def alternative(argument):
print("This is alternative",argument)
return alternative
@funA
def funB(argument):
print("This is funB",argument)
funB("test")
输出:
This is alternative test
以上的程序和下面这个程序是等价的:
def funA(func):
def alternative(argument):
print("This is alternative",argument)
return alternative
def funB(argument):
print("This is funB",argument)
funB=funA(funB)
funB("test")
通过funB()函数被funA()修饰,funB就被赋值为alternative,这意味这虽然我们在程序中显示调用的是funB函数,但是实际执行的是装饰器内嵌套的alternative函数
如果当前程序中,有多个(大于等于两个)函数被同一个装饰器函数修饰,这些函数带有的参数并不相等,此时的解决方法是利用*args和**kwargs作为装饰器内部嵌套函数的参数。
def funA(func):
def A(*args,**kwargs):
func(*args,**kwargs)
return A
@funA
def B(a1):
print("this is B, param:",a1)
@funA
def C(a1,a2):
print("this is C,param1:",a1,",param2:",a2)
输出:
this is B, param: test
this is C,param1: test1 ,param2: test2
参考:
https://www.zhihu.com/question/24863332
https://www.jianshu.com/p/ee82b941772a
http://c.biancheng.net/view/2270.html
https://www.jb51.net/article/158533.htm