ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python funtools 中的 lru_cache 和 singledispatch

2022-02-01 19:59:13  阅读:184  来源: 互联网

标签:Python htmlize cache print lru fibonacci time funtools singledispatch


摘自流畅的Python 第七章 函数装饰器和闭包

使用functools.lru_cache 做备忘

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

clockdeco.py

import time

def clock(func):
    def clocked(*args):
        t0 = time.time()
        result = func(*args)
        elapsed = time.time() - t0
        name = func.__name__
        arg_str = ', '.join(repr(arg) for arg in args)
        print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result))
        return result

    return clocked

我们使用斐波那契数列作为例子 观察
fibo_demo.py


from clockdeco import clock
import time

@clock
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-2) + fibonacci(n-1)

if __name__=='__main__':
    start = time.time()
    print(fibonacci(30))
    print(time.time() - start)

部分输出如下

...
[0.92167187s] fibonacci(24) -> 46368
[1.41511607s] fibonacci(25) -> 75025
[2.26029801s] fibonacci(26) -> 121393
[3.61707902s] fibonacci(27) -> 196418
[5.80630493s] fibonacci(28) -> 317811
[9.39297986s] fibonacci(29) -> 514229
[15.82301521s] fibonacci(30) -> 832040
832040
15.82302474975586


浪费时间的地方很明显:fibonacci(1) 调用了8次,fibonacci(5)调用了5次…
现在我们在代码中添加lru_cache

fibo_demo_lru.py


import functools
import time

from clockdeco import clock


@functools.lru_cache()  # 注意,必须像常规函数那样调用 lru_cache。这一行中有一对括 号:@functools.lru_cache()。这么做的原因是,lru_cache 可以 接受配置参数,稍后说明。
@clock  # 这里叠放了装饰器:@lru_cache() 应用到 @clock 返回的函数上。这样一来,执行时间减半了,而且 n 的每个值只调用一次函数:
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 2) + fibonacci(n - 1)


if __name__ == '__main__':
    start = time.time()
    print(fibonacci(30))
    print(time.time() - start)

部分输出如下

[0.00000119s] fibonacci(25) -> 75025
[0.00016594s] fibonacci(26) -> 121393
[0.00000000s] fibonacci(27) -> 196418
[0.00020003s] fibonacci(28) -> 317811
[0.00000072s] fibonacci(29) -> 514229
[0.00020981s] fibonacci(30) -> 832040
832040
0.00021791458129882812

由此可以看出使用lru_cache 优化的斐波那契 在计算的耗时方面极大程度的优化了

特别要注意,lru_cache可以使用两个可选的参数来配置。它的签名是

functools.lru_cache(maxsize=128, typed=False)

maxsize参数指定存储多少个调用的结果。缓存慢了之后,旧的结果会被扔掉,腾出空间。为了得到最佳性能,maxsize应该设为2的幂。
type参数如果设为True,把不同参数类型得到的结果分开保存,即把通常人为相等的浮点数和整数参数(如1和1.0)区分开。顺便说一下,因为lru_cache使用字典存储结果,而且键根据调用是传入的定位参数和关键字参数创建,所以被lru_cache装饰的函数,它的所有参数都必须是可散列的

单分派泛函数 singledispatch(重载)

假设我们在开发一个调试Web应用的工具,我们想生成HTML,显示不同类型的Python对象。我们可能会编写这样的函数

import html
def htmlize(obj):
    # repr() 函数将对象转化为供解释器读取的形式
    content = html.escape(repr(obj))
    return '<pre>{}</pre>'.format(content)

print(htmlize({1,2,3}))

这个函数使用与任何Python类型,但是现在我们想做个拓展,让它使用特别的方式显示某些类型

  • str: 把内部的换行符替换为’<br>\n’; 不使用<pre>,而是使用<p>
  • int: 以十进制和十六进制显示数字
  • list: 输出一个HTML列表,根据各个元素的类型进行格式化
<pre>{1, 2, 3}</pre>

因为Python不支持重载方法或函数,所以我们不能使用不同的签名定义htmlize变体,也无法使用不同的方式处理不同的数据类型

generic.py


from functools import singledispatch
from collections import abc
import numbers
import html
# @singledispatch 标记处理 object 类型的基函数。
@singledispatch
def htmlize(obj):
    content = html.escape(repr(obj))
    return '<pre>{}</pre>'.format(content)

# 各个专门函数使用 @«base_function».register(«type») 装饰。
@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)

# 可以叠放多个 register 装饰器,让同一个函数支持不同类型。
@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>'


print(htmlize({1,2,3}))
print('-'*40)
print(htmlize(abs))
print('-'*40)
print(htmlize('Heimlich & Co.\n- a game'))
print('-'*40)
print(htmlize(42))
print('-'*40)
print(htmlize(['alpha', 66, {3, 2, 1}]))

输出如下

<pre>{1, 2, 3}</pre>
----------------------------------------
<pre>&lt;built-in function abs&gt;</pre>
----------------------------------------
<p>Heimlich &amp; Co.<br>
- a game</p>
----------------------------------------
<pre>42 (0x2a)</pre>
----------------------------------------
<ul>
<li><p>alpha</p></li>
<li><pre>66 (0x42)</pre></li>
<li><pre>{1, 2, 3}</pre></li>
</ul>

标签:Python,htmlize,cache,print,lru,fibonacci,time,funtools,singledispatch
来源: https://blog.csdn.net/weixin_42817311/article/details/122764876

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有