标准库中的装饰器


Python内置了三个用于装饰方法的函数: property、classmethod和staticmethod

另一个常见的装饰器是functools.wraps,它的作用是协助构建行为良好的装饰器

使用functools.lru_cache做备忘

functools.lru_cache是非常实用的装饰器,它实现了备忘(memorization)功能。这是一项优化技术,它把耗时的函数的结果保存起来,避免传入相同的参数时重复计算。

其中LRU三个字母是“Least Recently Used”的缩写,表明缓存不会无限制增长,一段时间不用的缓存条目会被扔掉。

#生成第n个斐波纳契数,递归方式非常耗时,使用缓存实现,速度更快
import functools 
 
from clockdeco import clock 
 
@functools.lru_cache() # ➊ 
@clock  # ➋ 
def fibonacci(n): 
    if n < 2: 
        return n 
    return fibonacci(n-2) + fibonacci(n-1) 
 
if __name__=='__main__': 
    print(fibonacci(6))

➊ 注意,必须像常规函数那样调用lru_cache。这一行中有一对括号:@functools.lru_cache()。这么做的原因是,lru_cache可以接受配置参数,稍后说明。
➋ 这里叠放了装饰器:@lru_cache()应用到@clock返回的函数上。

运行得到的结果如下,这样一来,执行时间减半了,而且n的每个值只调用一次函数

[0.00000119s] fibonacci(0) -> 0 
[0.00000119s] fibonacci(1) -> 1 
[0.00010800s] fibonacci(2) -> 1 
[0.00000787s] fibonacci(3) -> 2 
[0.00016093s] fibonacci(4) -> 3 
[0.00001216s] fibonacci(5) -> 5 
[0.00025296s] fibonacci(6) -> 8

单分派泛函数

#生成HTML的htmlize函数,调整了几种对象的输出
>>> htmlize({1, 2, 3})  #➊ 
'<pre>{1, 2, 3}</pre>' 
>>> htmlize(abs) 
'<pre>&lt;built-in function abs&gt;</pre>' 
>>> htmlize('Heimlich & Co.\n- a game')  #➋ 
'<p>Heimlich &amp; Co.<br>\n- a game</p>' 
>>> htmlize(42)  #➌ 
'<pre>42 (0x2a)</pre>' 
>>> print(htmlize(['alpha', 66, {3, 2, 1}]))  #➍ 
<ul> 
<li><p>alpha</p></li> 
<li><pre>66 (0x42)</pre></li> 
<li><pre>{1, 2, 3}</pre></li> 
</ul>

➊ 默认情况下,在<pre>中显示HTML转义后的对象字符串表示形式。
➋ 为str对象显示的也是HTML转义后的字符串表示形式,不过放在<p></p>中,而且使用<br>表示换行。
➌ int显示为十进制和十六进制两种形式,放在<pre>中。
➍ 各个列表项目根据各自的类型格式化,整个列表则渲染成HTML列表。

因为Python不支持重载方法或函数,所以我们不能使用不同的签名定义htmlize的变体,也无法使用不同的方式处理不同的数据类型。
在Python中,一种常见的做法是htmlize变成一个分派函数,使用一串if/elif/elif,调用专门的函数,如htmlize_str、htmlize_int,等等。这样不便于模块的用户扩展,还显得笨拙:时间一长,分派函数htmlize会变得很大,而且它与各个专门函数之间的耦合也很紧密

Python 3.4新增的functools.singledispatch装饰器可以把整体方案拆分成多个模块,甚至可以为你无法修改的类提供专门的函数。使用@singledispatch装饰的普通函数会变成泛函数(generic function):根据第一个参数的类型,以不同方式执行相同操作的一组函数。

 singledispatch创建一个自定义的htmlize.register装饰器,把多个函数绑
在一起组成一个泛函数
from functools import singledispatch 
from collections import abc 
import numbers 
import html 
 
@singledispatch  #➊ 
def htmlize(obj): 
    content = html.escape(repr(obj)) 
    return '<pre>{}</pre>'.format(content) 
 
@htmlize.register(str)  #➋ 
def _(text):            #➌ 
    content = html.escape(text).replace('\n', '<br>\n') 
    return '<p>{0}</p>'.format(content) 
 
@htmlize.register(numbers.Integral)  #➍ 
def _(n): 
    return '<pre>{0} (0x{0:x})</pre>'.format(n) 
 
@htmlize.register(tuple)  #➎ 
@htmlize.register(abc.MutableSequence) 
def _(seq):
inner = '</li>\n<li>'.join(htmlize(item) for item in seq) 
    return '<ul>\n<li>' + inner + '</li>\n</ul>'

➊ @singledispatch标记处理object类型的基函数。
➋ 各个专门函数使用@«base_function».register(«type»)装饰。
➌ 专门函数的名称无关紧要;_是个不错的选择,简单明了。
➍ 为每个需要特殊处理的类型注册一个函数。numbers.Integral是int的虚拟超类。
➎ 可以叠放多个register装饰器,让同一个函数支持不同类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值