如何理解Python中的装饰器?
Posted Qianren
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何理解Python中的装饰器?相关的知识,希望对你有一定的参考价值。
问题1.为什么使用装饰器?
扩展函数功能,减少冗余代码。
举个栗子,以下这段代码:
def add(x,y):
return x+y
def sub(x,y):
return x-y
如果想在每个函数执行时输出"加减乘除"
笨办法是在每个函数都写明:
def add(x,y):
print(‘加减乘除‘)
return x+y
def sub(x,y):
print(‘加减乘除‘)
return x-y
但函数很多的话工作量很大
因此,若使用装饰器:
def decorator(fun):
def wrapper(*args,**kw):
print(‘加减乘除‘)
return fun(*args,**kw)
return wrapper
@decorator
def add(x,y):
return x+y
@decorator
def sub(x,y):
return x-y
print(add(1,2))
就可以很明显的减少工作量
对于如下的装饰器代码,引入问题2
def decorator(fun):
def wrapper(*args,**kw):
print(‘加减乘除‘)
return fun(*args,**kw)
return wrapper
问题2.如何理解装饰器的代码?为什么要返回一个函数?为什么一定要在装饰器里定义函数?只写一个函数不行么?
在我知道了装饰器的功能后,一开始我认为只要写一个函数,然后将要扩展的函数作为参数,以及扩展函数所需的参数传入这个函数就行了
于是有了如下的测试代码:
def func(func1,*args,**kw):
print(‘执行前‘)
func1(*args,**kw)
print(‘执行后‘)
return func
@func
def func1():
print(‘123‘)
跑一下你就会惊奇的发现,我没有调用func1,但是却执行了三个print
其实这是因为@func加上def func1() 相当于func1=fun(func1)
如果你在上面的代码加上一句
func1()
就会报错,说是未传参
错误原因就是因为-----@func + def func1()等于func1=fun(func1),从而有func1=func
因此此时func1与func可以看做指向同一个函数了,fun需要一个函数参数,因此你直接写fun1()当然会报错
到现在我想已经很好理解了
既然你的目的是扩展函数的功能,那么有两个前提条件:
1.要在不改变函数名以及函数参数表的同时往这个函数里面添加功能
2.要在函数调用的时候代码才能执行
原因如下:
1.如果你只用一个函数实现扩展,那么这个函数的参数表里面必然会用一个参数用来传入原函数,那么当你把这个函数再赋给原函数后,
原函数的参数表就会发生变化,这显然不符合只扩展不改变的要求
2.上述代码在函数未调用前就执行了,显然不是我们想要的
因此,必须在函数里面再声明一个函数,用这个函数实现扩展(因为在python函数里声明的函数是不会执行的)
现在再看上面的这段代码,应该就能理解了:
#decorator函数的功能就是起一个装饰的功能,你传一个函数过来,我帮你装饰
#装饰完成后,再返回我装饰好了的函数
def decorator(fun):
#这里的wrapper就是打包,或者说是原函数功能的扩展,这里面除了调用了
#原函数,返回原函数处理过的值(当然了,返回值也可以变化),同时增加
#了新的语句
def wrapper(*args,**kw):
print(‘加减乘除‘)
return fun(*args,**kw)
return wrapper
到这里应该很明了了
对于:
@decorator
def add(x,y):
return x+y
其实就相当于:add=decorator(add)
而decorator(add)等于wrapper
就是说add=wrapper
不信你可以添加这段代码试试:
print(add.__name__)
看看add实际指向的是不是wrapper函数
小结一下:
为什么返回函数?因为我们希望原函数指向一个新函数,新函数扩展了原函数功能
为什么要在decorator里面定义函数?因为python的函数里面定义的函数不会执行
只写一个函数不行么?同上,只写一个函数必然会执行,且改变了原函数参数,一定不行
总之
wrapper就是你实际想要的具有扩展功能的新函数
你所有做的就是让旧函数指向新的函数,同时兼容原来调用的旧函数的参数表
好了,到这里你还会问
问题3:如果新的函数需要其它参数(不是原函数那些参数,而是其它需要传入的)怎么办?
直接上代码:
def fun(text):
def decorator(fun):
def wrapper(*args,**kw):
print(‘加减乘除‘,text)
return fun(*args,**kw)
return wrapper
return decorator
@fun(‘好玩‘)
def add(x,y):
return x+y
在原来decorator的基础上再包一层函数用以传入参数即可
此时有 add=fun(‘好玩‘)(add)
先调用fun,返回decorator,再调用decorator,返回wrapper,于是有add=wrapper
参考资料:
1.https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584
2.https://www.zhihu.com/question/26930016/answer/1047233982
以上是关于如何理解Python中的装饰器?的主要内容,如果未能解决你的问题,请参考以下文章