Python 3.7.1 模块 functools

本文深入探讨了Python中@functools.lru_cache装饰器的使用,解释了如何利用LRU缓存策略优化函数调用,减少重复计算,提高效率。通过具体示例展示了缓存在动态编程和Web内容获取中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源代码: Lib / functools.py

未完…


该模块用于高阶函数:作用于或返回其他函数的函数。通常,任何可调用对象都可以被视为用于此模块的函数。

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

译者注:这里有一个简单的 例子

用一个memoizing callable来包装一个函数,它可以为最近的调用节省maxsize空间。当使用相同的参数定期调用或绑定昂贵的I/O函数时,它可以节省时间。

由于字典用于缓存结果,因此函数的位置和关键字参数必须是可哈希的。

不同的参数模式可以被认为是具有单独的高速缓存条目的不同调用。例如,f(a=1, b=2)f(b=2, a=1)的关键字参数顺序不同,并且可能有两个单独的缓存条目。

如果将maxsize=None,则禁用LRU(最近最少使用)功能,并且缓存可以无限制地增长。当maxsize是2的幂时,LRU功能表现最佳。

如果typed=True,则将分别缓存不同类型的函数参数。例如,f(3)f(3.0)将被视为产生截然不同的结果不同的调用。

为了帮助测量缓存的有效性并调整maxsize 参数,包装函数使用一个cache_info() 函数进行检测,该函数返回一个命名元组,显示hits,misses, maxsize,currsize。在多线程环境中,hitsmisses是近似值。

装饰器还提供cache_clear()功能用来清除或使缓存无效的。

可以通过__wrapped__属性访问原始基础函数 。这对于内省,绕过缓存或使用不同的缓存重新包装函数非常有用。

当最近的调用是即将到来的调用的最佳预测时,LRU(最近最少使用的)缓存工作得最好(例如,新闻服务器上的最流行的文章往往每天更换)。缓存的大小限制可确保缓存不会增长,而不受长时间运行的进程(如Web服务器)的限制。

通常,只有在要重用以前计算的值时才应使用LRU高速缓存。因此,,需要在每次调用时创建不同的可变对象的函数,或者诸如time()或random()之类的不纯函数使用LRU是没什么效果的。

静态Web内容的LRU缓存示例:

@lru_cache(maxsize=32)
def get_pep(num):
    'Retrieve text of a Python Enhancement Proposal'
    resource = 'http://www.python.org/dev/peps/pep-%04d/' % num
    try:
        with urllib.request.urlopen(resource) as s:
            return s.read()
    except urllib.error.HTTPError:
        return 'Not Found'

>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
...     pep = get_pep(n)
...     print(n, len(pep))

>>> get_pep.cache_info()
CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)

使用高速缓存实现 动态编程 技术有效计算Fibonacci数的示例 :

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)

版本3.2中的新功能。

版本3.3中已更改:添加了typed选项。

译者实例
为了看出lru的效果,我们使用普通的fib函数和使用LRU的fib函数做对比:

def lru_test():
    def fib(n):
        if n < 2:
            return n
        return fib(n - 1) + fib(n - 2)

    @lru_cache(None)
    def fib2(n):
        if n < 2:
            return n
        return fib2(n - 1) + fib2(n - 2)

    import time
    starttime = time.time()
    print("before")
    print(fib(34))
    print("total",str(time.time() - starttime))
    print("-".center(50,'*'))
    starttime = time.time()
    print("before")
    print(fib2(450))
    print(fib2.cache_info())
    print("total", str(time.time() - starttime))
lru_test()
# 输出结果
before
5702887
total 1.9108920097351074
************************-*************************
before
4953967011875066473162524925231604047727791871346061001150551747313593851366517214899257280600
CacheInfo(hits=448, misses=451, maxsize=None, currsize=451)
total 0.0009984970092773438

可以看到,普通的fib求34的时候已经花费了1.9s,而经过LRU的fib在求450的时候只有不到0.001s的时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值