Python 参数化装饰器

Posted arthurlzyw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 参数化装饰器相关的知识,希望对你有一定的参考价值。

解析代码中的装饰器的时候,Python把被装饰的函数作为第一个参数传给装饰器函数。如果想让装饰器接受其他参数就需要创建一个装饰器工厂函数,再把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。


最简单的装饰器就如示例1中的register:

 


 

(示例1)

# BEGIN REGISTRATION_ABRIDGED
registry = []

def register(func):
    print(running register(%s) % func)
    registry.append(func)
    return func

@register
def f1():
    print(running f1())

print(running main())
print(registry ->, registry)
f1()
# END REGISTRATION_ABRIDGED

 

为了便于启动和禁用register执行的函数注册功能,我们可以为它提供一个可选的active参数,设置为False时,不注册被装饰函数。实现方式如示例2

(从概念上看,这个新的register函数不是装饰器,而是装饰器工厂函数。调用它会返回真正的装饰器,这个才是应用到目标函数上的装饰器)


 

(示例2)

# BEGIN REGISTRATION_PARAM

registry = set()  # <1>

def register(active=True):  # <2>
    def decorate(func):  # <3>
        print(‘running register(active=%s)->decorate(%s)‘
              % (active, func))
        if active:   # <4>
            registry.add(func)
        else:
            registry.discard(func)  # <5>

        return func  # <6>
    return decorate  # <7>

@register(active=False)  # <8>
def f1():
    print(‘running f1()‘)

@register()  # <9>
def f2():
    print(‘running f2()‘)

def f3():
    print(‘running f3()‘)

# END REGISTRATION_PARAM

< 1 >    registry现在是一个set对象,这样添加和删除函数的速度更快。

< 2 >    register接受一个可选的关键字参数。

< 3 >    decorate这个内部函数是真正的装饰器,它的参数是一个函数。

< 4 >    只有active参数的值(从闭包中获取)是True时才注册func。

< 5 >    如果active不为真,而且func在register中,那么把它删除。

< 6 >    decorate是一个装饰器,必须返回一个函数。

< 7 >    register是装饰器工厂函数,因此返回decorate。

< 8 >    @register工厂函数必须作为函数调用,并且传入所需参数。

< 9 >    即使不传入参数,register也必须作为函数调用(@register),就是要返回真正的装饰器decorate。

这里的关键是,register()要返回decorate,然后把它应用到被装饰的函数上。

示例2中的代码在registeration_param.py模块中。导入后得到结果如下:

>>>import registration_param
running register(active=False)->decorate(<function f1 at 0x03320F18>)
running register(active=True)->decorate(<function f2 at 0x03320ED0>)
>>>registration_param.registry 
{<function f2 at 0x03320ED0>}

 

这里的f2函数在registry中;但是f1不在其中,因为传给register装饰器工厂函数的参数是active=False,所以应用到f1上的decorate没有把它添加到register中。

如果不使用@句法,那么就要像常规函数那样使用register;若想把f添加到register中,则装饰f函数的句法是register()( f );如果不想添加或者把它删除的话,句法是register(active=False)( f )。示例3演示如何把函数添加到register中,以及如何从中删除函数。

 


 

 

示例3

>>>from registration_param import*
>>>registry                                #<1>
{<function f2 at 0x03320ED0>} 
>>>register()(f3)                        #<2>
running register(active=True)->decorate(<function f3 at 0x03320F60>) <function f3 at 0x03320F60> 
>>>registry                                #<3>
{<function f2 at 0x03320ED0>, <function f3 at 0x03320F60>} 
>>>register(active=False)(f2)     #<4>
running register(active=False)->decorate(<function f2 at 0x03320ED0>) <function f2 at 0x03320ED0>  
>>>registry                                #<5>
{<function f3 at 0x03320F60>}

 

< 1 >    导入这个模块的时候,f2在registry中。

< 2 >    register()表达式返回decorate,然后把它应用到f3上。

< 3 >    前一行把f3添加到registry中。

< 4 >    这次调用从registry中删除f2。

< 5 >    确认registry。

 

参数化装饰器的原理非常复杂,参数化装饰器通常会把被装饰的函数替换掉,而且结构上需要多一层嵌套。

 


以上是关于Python 参数化装饰器的主要内容,如果未能解决你的问题,请参考以下文章

Python装饰器

柯里化与python装饰器

python笔记--3--函数生成器装饰器函数嵌套定义函数柯里化

python的装饰器

Go的魅力, 函数式(柯里化, 闭包, 高阶函数), Python@装饰器, 封装

Python进阶装饰器(Decorator)