python基础-装饰器笔记

内容来自流畅的python

虽然是python基础,但是看的时候感觉有种恍然大悟的感觉。

一、概述

函数装饰器用于在源码中“标记”函数,以某种方式增加函数的行为。这是一项强大的功能,但是若想要掌握,必须理解闭包。

除了在装饰器中有用处之外,闭包还是回调异步编程和函数式编程风格的基础。

◆◆◆◆◆

二、装饰器的基础知识

装饰器是可调用的对象,其参数是另一函数(被装饰的函数)。

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

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

两个代码执行完之后的结果都为decorate(target)返回的内容。

使用装饰器把函数替换成另一个函数

定义一个装饰器deco返回inner函数对象

使用deco装饰target

下面进行结果输出:

调用被装饰的target其实会运行inner

三、Python何时执行装饰器

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

执行结果如下:

running register()

running register()

----running----

registry -> [,]

running f1()

running f2()

running f3()

从结果可以看出,register在模块中其他函数之前运行了两次。调用register时,传给他的参数是被装饰的函数,例如

图片 1.png

在其他文件中导入的话可看到结果

图片 2.png

综上所述:函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。

四、使用装饰器改进“策略”模式

定义一个装饰器promotion用于给列表promos存储内容。

充分利用了装饰器的执行顺序。

优点:

促销策略中无需使用特殊的名称表示(一般用_promo结尾表示为折扣策略)

@promotion装饰器突出了被装饰的函数的作用,便于临时禁用某个促销策略,只需要把装饰器注释掉

促销折扣策略可用在其他模块中定义,只需要使用@promotion装饰即可。

五、实现一个简单的装饰器

一个简单的装饰器,输出函数的运行时间

该函数实现了

记录出事时间t0

调用传入的函数保存结果

计算经过的时间

格式化收集的数据,然后打印出来

返回第二步保存的结果

装饰器的典型行为:把装饰的函数替换成为新函数,二者接受相同的参数,而且(通常)返回被装饰的函数本该返回的值,同时还会做一些额外操作。

但是会发现我们无法看到被装饰的函数的name__和__doc属性

所以对上文中的clock进行一定的修改,使其支持关键字还有name__和__doc属性

六、functools.lru_cache

functools.lru_cache实现了备忘功能,它能把耗时的函数的结果保存起来,避免传入相同的参数时重复计算

使用常规思路写一个斐波纳切数

可以看出,除了最后一行,其余输出都是clock装饰器生成的。fibonacci(1)调用了8次,fibonacci(2)调用了5次。

下面使用lru_cache()

可以看出,n的每个值只调用一次函数

lru_cache还要两个参数可以调用:

maxsize表示抗议存储多少个调用的结果;

typed表示是否把不同参数类型得到的结果分开保存;

七、functools.singledispatch

首先看一个简单的函数

html.escape的作用的是把html文件中的特殊字符(&,,",'等)转换为HTML-safe字符。现在想要对这个函数做一个扩展

对于这个需求的解决思路一般是用一长串的if/elif/elif来调用专门的函数解决(当判断输入的内容为str的时候调用例如htmlize_str的方法)。这样不便于模块的拓展,时间一长,htmlize会变得很大,而且与各个专门函数之间的耦合也很紧密。

Python3.4新增的functools.singledispatch装饰器可以把整体方案拆分成多个模块。使用它装饰的普通函数会变成泛函数:根据第一个参数的类型,以不同方式执行相同操作的一组函数。

@singledispatch标记处理object类型的基函数

各个专门函数使用@《base_function》.register(《type》)装饰

由于专门函数的名称没有意义,所以用下划线_表示

number.Integral是int的虚拟超类,和abc.MutableSequence一样都是抽象基类

最后一个函数表明可以叠放多个register装饰器,让同一个函数支持不同类型

在一个类中为同一个方法定义多个重载变体(def a ,def b,def c),比在一个函数里面使用一长串if/elif/elif块要好。@singledispath的优点是支持模块化扩展,各个模块可以为它支持的各个类型注册一个专门的函数。

八、叠放装饰器

装饰器是函数,所以可以组合起来使用。(在被装饰的函数上应用装饰器)

上面和下面两者是一样的

九、参数化装饰器

Python把装饰的函数作为第一个参数传递给装饰器函数,如果需要让装饰器接受其他的参数的话,需要创建一个装饰器工厂函数,把参数传递给它,返回一个装饰器,然后再把它应用到要装饰的函数上。将第三章的例子改写一下:

与之前的例子进行对比可以发现decorate这个内部函数是真正的装饰器,它的参数是一个函数,它是一个装饰器,所以必须返回一个函数

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

@register工厂函数必须作为函数调用,并且传入所需的参数,如果有默认值那也需要作为函数调用【@register()】,即要返回真正的装饰器decorate

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

图片 3.png

从结果可以看到只有f2加入到了集合中。

装饰器其实就是函数的调用,所以如果不使用@的话

修改一下第五章中clock装饰器,给它添加一个功能:让用户输入一个格式化字符串,控制被装饰函数的输出

将之前的格式化输出当初默认的参数输入。

clock是参数化装饰器的工厂函数。decorate是真正的装饰器,clocked包装被装饰的函数。

**locals()是为了在fmt中引用clocked的局部变量。

图片 4.png

图片 5.png

下面修改下格式化输出的内容:

图片 6.png

图片 7.png

只要修改的格式化输出的内容包含在clocked的局部变量就可以正常输出了。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180916G0OB3J00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券