python编程之函数装饰器语法原理以及参数传递详解

系统 1597 0

       笔者之前写过一篇简单介绍python函数装饰器用法的文章,本文便不再赘述。简而言之,pythong函数装饰器是一种通过特定语法,给函数额外增加一层逻辑,以实现相应目的的一种语法结构。本文将介绍装饰器的原理、被装饰函数是如何以参数的形式传入到装饰器函数内部的,以及实现给装饰器函数设定参数的参数传递原理。

            
              def decorator(func):
    def wrapper(*args,**kargs):
        print('I love Python!')
        func()
    return wrapper

@decorator
def fun():
    print('test')
            
          

       如上代码,是一个典型的关于函数装饰器的例子,但遗憾的是,这个例子其实说明不了太多的东西,仅仅只是介绍了函数装饰器的最简单的一种应用方式而已。为了理解函数装饰器,我们从最简单最直接的装饰器构造方式,一步步展开。

            
              def decorator(func):
    print('just for testing')
    return func

@decorator
def fun():
    print('test')
            
          

       上面的代码实际上也可以顺利执行,当脚本运行到@语句时,便会把函数fun以参数的形式传给decorator,然后装饰器函数decorator便会运行,运行的结果就是执行print,然后将fun返回给fun自己,因为这时装饰器函数返回的函数就是fun本身,所以当我们再调用fun的时候,fun的行为和其定义的一样,没有任何变化。通过这个例子我们知道,实际上函数装饰器的语法就是:通过@声明装饰器函数,被装饰函数作为参数传给装饰器函数,然后装饰器函数会执行,执行的最后需要返回一个函数给被装饰函数,即被装饰的函数重绑定到装饰器函数返回函数。换言之,@语句会直接执行装饰器函数,然后返回一个函数给被装饰器函数,而这个装饰器函数内部的执行过程是任意的,并不一定需要定义wrapper函数,就像上面的代码所示。

       当然,上面的代码仅仅是为了说明装饰器的原理,一般并不会这么使用。一般情况下,为了给函数添加额外的逻辑层,我们会在装饰器函数内定义一个wrapper函数,在其内部实现额外的逻辑,然后将wrapper函数返回以重新绑定到原函数。更细致的说, 装饰器函数自己必须是个可调用的函数或者返回一个可调用函数,我们说这个可直接调用或者返回的可直接调用的函数才是真正的装饰器,真正的装饰器函数是单参的,参数就是被装饰函数 。这里说的装饰器函数也可以是一个返回一个可调用函数的函数,这样的机制可以让我们实现给装饰器函数设定参数,当然这时的装饰器函数实际上已经不是真正的装饰器了,真正的装饰器是其返回的可调用函数,对此后面我们细讲。

       然后讲一下关于被装饰函数的参数的传递。如果被装饰函数有参数,严格的讲,是如果我们调用被装饰函数时,我们给被装饰函数传递了参数,那么这个参数是如何传给装饰器函数内部的呢?看如下代码。

            
              def decorator_1(func):
    def wrapper(*args,**kargs):
        func()
    return wrapper

@decorator_1
def fun_1(x,y):
    print(x+y)

fun_1(3,4) # output: TypeError: fun() missing 2 required positional arguments: 'x' and 'y'


def decorator_2(func):
    def wrapper(*args,**kargs):
        func(*args,**kargs)
    return wrapper

@decorator_2
def fun_2(x,y):
    print(x+y)

fun_2(3,4) # output: 7
            
          

       如上代码所示,如果我们要给被装饰函数传递参数,那么其就应该按照第二种写法实现。因为实际上,被装饰函数的参数会自动传给装饰器函数中的被返回的函数,所以我们应该给wrapper函数设定*args,**args的形式,以保证fun_2被装饰函数的任何参数形式其都能接收,此外,wrapper函数中的func函数的参数形式也应该同样设定,就是为了可以接收任何函数形式,包括位置参数和关键字参数。

       最后我们将讲解如何给装饰器函数设定参数。先看参考代码。

            
              def outer(number):
    def decorator_1(func):
        def wrapper(*args,**kargs):
            print('I love Python!')
            func(*args,**kargs)
        return wrapper
    
    def decorator_2(func):
        def wrapper(*args,**kargs):
            print('I love Pandas!')
            func(*args,**kargs)
        return wrapper
    
    if number==0:
        return decorator_1
    else:
        return decorator_2

@outer(0)
def fun1():
    print('test')

@outer(1)
def fun2():
    print('test')
            
          

       因为装饰器语法有如下的等价形式:

@decorator

def fun():

    pass

等价于

fun=decorator(fun)

即这里的decorator必须是一个可调用函数,但是如果其是一个可执行函数,那么其返回的结果必须是一个可调用函数,这时,有如下等价关系:

@decorator(args)

def fun():

    pass

等价于

fun=decorator(args)(fun)

因为decorator(args)本身就是一个可直接执行的函数,那么其必须返回一个可调用函数,假设为deco,则上面进一步等价为fun=deco(fun),所以这时,实际上deco才是真正的装饰器函数。

       下面就好理解上面的代码了。因为outer(0)、outer(1)都是直接可执行的函数,其执行后返回的结果分别为decorator_1、decorator_2,所以实际上有如下等价关系:

@outer(0)
def fun1():
    print('test')

@outer(1)
def fun2():
    print('test')

等价于

@decorator_1
def fun1():
    print('test')

@decorator_2
def fun2():
    print('test')

这样,就不难理解,为何被装饰函数可以作为参数传递给outer函数内部的decorator函数,所以,这时其实outer内部的decorator函数才是真正的装饰器函数。

       最后总结一下本文讲的内容:

1、@decorator语法和@deco(a,b)语句,相当于直接执行decorator(fun)和deco(a,b)(fun),并把执行结果返回给fun;

2、被装饰函数的参数会自行的直接传给真正的装饰器函数内的被返回的可调用函数,而被返回的可调用函数最好通过*args,**kargs的形式设定参数,以接收任何的参数形式;

3、在理解1的基础上,通过外加一层函数,以实现给装饰器函数自定义参数。


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论