如何理解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中的装饰器?的主要内容,如果未能解决你的问题,请参考以下文章

如何理解python装饰器

python使用上下文对代码片段进行计时,非装饰器

理解python中的装饰器

如何理解python中的装饰器, 这篇文章就够了!

如何理解Python装饰器

如何在python中使用条件装饰器?