函数装饰器

装饰器基础知识

装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。

假如有个名为decorate的装饰器:

@decorate

def target():

    print('running target()')

上述代码的效果与下述写法一样:

def target():

    print('running target()')

target=decorate(target)

这两种写法的最终结果一样:上述两个代码片段执行完毕后得到的target不一定是原来那个target函数,而是decorate(target)返回的函数。

python何时执行装饰器

装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行,这通常是在导入(即python加载模块时)

上述脚本运行得到的输出如下:


上述示例主要想强调,函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行,考虑到装饰器在真实代码中的常用调用方式,上述示例有两个不寻常的地方:

1、装饰器函数与被装饰的函数在同一个模块中定义。实际情况是,装饰器常在一个模块中定义,然后应用到其他模块的函数中。

2、registry装饰器返回的函数与通过参数传入的相同。实际上,大多数装饰器会在内部定义一个函数,然后将其返回。

变量的作用域规则

例:一个函数,读取一个局部变量和一个全局变量


在上述例子中,python在编译函数的定义体时,它判断b是局部变量,因为在函数中给它赋值了,python会尝试从本地环境获取b。后面调用f2(3)时,f2的定义体会获取并打印局部变量a的值,但是尝试获取局部变量b时,发现b没有绑定值

实现一个简单的装饰器

下例定义了一个装饰器,它会在每次调用被装饰的函数时计时,然后把经过的时间、传入的参数和调用的结果打印出来。

使用clock装饰器


上述例子中实现的clock装饰器有几个缺点:不支持关键字参数,而且遮盖了被装饰函数的__name__和__doc__属性。下述例子使用functools.wraps装饰器把相关的属性从func复制到clocked中。

参数化装饰器

解析源码中的装饰器时,python把被装饰的函数作为第一个参数传给装饰器函数。那怎么让装饰器接受其他参数呢?答案是:创建一个装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。

为了便于启用或禁用register执行的函数注册功能,我们为它提供一个可选的active参数,设为false时,不注册被装饰的函数,示例如下,从概念上看,这个新的register函数不是装饰器,而是装饰器工厂函数。调用它会返回真正的装饰器,这才是应用到目标函数上的装饰器。


在上述例子中:

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

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

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

4、decorate是装饰器,必须返回一个函数。

5、register是装饰器工厂函数,因此返回decorate。

6、register是装饰器工厂函数,因此返回decorate。

7、@register工厂函数必须作为函数调用,并且传入所需的函数。

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

参数化clock装饰器


在上述示例中:

1、clock是参数化装饰器工厂函数。

2、decorate是真正的装饰器。

3、clocked包装被装饰的函数。

4、这里使用**locals()是为了在fmt中引用clocked的局部变量。

你可能感兴趣的:(函数装饰器)