关于python:深入理解Python的functoolslrucache装饰器

4次阅读

共计 3748 个字符,预计需要花费 10 分钟才能阅读完成。

在 Python 中,有许多内置的装璜器能够用来加强函数或者类的性能。其中之一就是 functools.lru_cache 装璜器。这是一个十分有用的装璜器,它能够帮忙咱们优化递归函数,防止反复计算曾经计算过的值。在这篇文章中,咱们将探讨 functools.lru_cache 的工作原理以及如何应用它。

一、什么是 functools.lru_cache

functools.lru_cache 是 Python 规范库中 functools 模块的一部分。lru_cache 装璜器能够用来为一个函数增加一个缓存零碎。这个缓存零碎会存储函数的输出和对应的输入。如果函数被调用,并且给出了曾经缓存过的输出,那么函数就不会从新计算,而是间接从缓存中获取对应的输入。

LRU 是“Least Recently Used”的缩写,意思是“最近起码应用”。LRU 缓存就是一种缓存淘汰算法,当缓存达到预设的容量下限时,会优先淘汰最近起码应用的数据。

from functools import lru_cache

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

print(fib(10))  # 输入:55

在下面的例子中,咱们定义了一个求斐波那契数列的函数,并且应用 @lru_cache(maxsize=None) 装璜器对其进行了装璜。而后咱们调用 fib(10),失去后果 55。实际上,因为应用了缓存,fib 函数在求解过程中,对于同样的参数只进行了一次计算。

二、如何应用 functools.lru_cache

要应用 functools.lru_cache 装璜器,你只须要在你的函数定义之前增加 @functools.lru_cache 行。这会让 lru_cache 装璜器晓得你心愿为这个函数增加一个缓存零碎。

lru_cache 装璜器有两个可选参数:

  • maxsize:这个参数用来设置缓存的大小。如果你设置了这个参数,缓存的大小就会被限度在这个值之内。如果你不设置这个参数,或者将其设置为 None,那么缓存的大小就没有下限。
  • typed:如果你将这个参数设置为 True,那么 lru_cache 就会依据输出参数的类型别离进行缓存。也就是说,11.0 只管在 Python 中是相等的,但它们会被当成两个不同的输出进行缓存。默认状况下,typed 参数是 False
from functools import lru_cache

@lru_cache(maxsize=128, typed=False)
def add(x, y):
    print(f"Calculating: {x} + {y}")
    return x + y

print(add(1, 2))  # 输入:Calculating: 1 + 2 \n 3
print(add(1, 2))  # 输入:3
print(add(1.0, 2.0))  # 输入:Calculating: 1.0 + 2.0 \n 3.0
print(add(1.0, 2.0))  # 输入:3.0

在下面的代码中,咱们定义了一个加法函数 add,并应用 lru_cache 装璜器对其进行装璜。咱们能够看到,当咱们第二次调用 add(1, 2)add(1.0, 2.0) 时,add 函数并没有从新进行计算,而是间接从缓存中获取了后果。

三、functools.lru_cache 的用处

functools.lru_cache 能够用于优化那些具备反复计算的递归函数,或者计算成本较高的函数。通过保留曾经计算过的值,functools.lru_cache 可能防止反复的计算,从而进步程序的运行效率。

例如,求解斐波那契数列就是一个典型的应用场景。在没有优化的状况下,求解斐波那契数列的工夫复杂度是指数级别的。然而,如果咱们应用 functools.lru_cache 对其进行优化,那么咱们就能够将其工夫复杂度升高到线性级别。

此外,functools.lru_cache 还能够用于缓存那些对数据库或者文件系统的反复查问,从而进步程序的性能。

须要留神的是,functools.lru_cache 并不适宜所有的场景。因为 functools.lru_cache 是通过空间换取工夫的形式来进步程序的性能的,所以,如果你的程序运行在内存无限的环境中,或者你的函数有大量的不同输出,那么应用 functools.lru_cache 可能会导致内存耗费过大。此外,如果你的函数有副作用,或者依赖于内部状态,那么 functools.lru_cache 也可能无奈正确地工作。在这些状况下,你可能须要寻找其余的优化策略。

总的来说,functools.lru_cache 是一个十分弱小的工具,它可能帮忙咱们优化代码,进步程序的性能。当你在编写一个计算密集型或者须要大量反复计算的函数时,无妨思考应用 functools.lru_cache 对其进行优化。

四、深刻了解 functools.lru_cache

当咱们将 functools.lru_cache 利用到函数上时,每次调用函数,它都会查看其参数是否曾经在缓存中。如果在缓存中,它将返回缓存的后果,而不须要从新计算。如果没有在缓存中,那么函数将被调用并且后果将被增加到缓存中。当缓存满了,起码应用的条目将被摈弃。

以下是一个了解 functools.lru_cache 工作形式的例子:

from functools import lru_cache

@lru_cache(maxsize=3)
def foo(n):
    print(f"Running foo({n})")
    return n

print(foo(1))  # 输入:Running foo(1) \n 1
print(foo(2))  # 输入:Running foo(2) \n 2
print(foo(3))  # 输入:Running foo(3) \n 3
print(foo(1))  # 输入:1
print(foo(2))  # 输入:2
print(foo(3))  # 输入:3
print(foo(4))  # 输入:Running foo(4) \n 4
print(foo(1))  # 输入:Running foo(1) \n 1

在这个例子中,咱们设定 maxsize=3,也就是只缓存最近的三个后果。当咱们间断调用 foo(1)foo(2)foo(3) 时,这三个后果都被缓存了下来。再次调用这三个函数时,因为后果曾经在缓存中,函数并没有被从新执行。然而当咱们调用 foo(4) 时,因为缓存已满,所以最早被缓存的 foo(1) 的后果被移除了。再次调用 foo(1) 时,函数须要被从新执行。

这个例子阐明了 functools.lru_cache 的 LRU 个性:当缓存达到下限时,最近起码应用的缓存会被移除。

五、清理和查看缓存

functools.lru_cache 还提供了两个办法用于清理和查看缓存:cache_clearcache_info

cache_clear 办法能够清空所有的缓存。例如,在下面的 foo 函数中,咱们能够通过 foo.cache_clear() 来清空所有的缓存。

cache_info 办法返回一个命名元组,形容了缓存的状态。它蕴含以下几个字段:hitsmissesmaxsizecurrsize。其中,hitsmisses 别离示意缓存命中和未命中的次数,maxsize 示意缓存的最大容量,currsize 示意以后缓存的使用量。

from functools import lru_cache

@lru_cache(maxsize=3)
def foo(n):
    print(f"Running foo({n})")
    return n

foo(1)
foo(2)
foo(3)
foo(4)
print(foo.cache_info())  # 输入:CacheInfo(hits=0, misses=4, maxsize=3, currsize=3)

foo(4)
print(foo.cache_info())  # 输入:CacheInfo(hits=1, misses=4, maxsize=3, currsize=3)

foo.cache_clear()
print(foo.cache_info())  # 输入:CacheInfo(hits=0, misses=0, maxsize=3, currsize=0)

在这个例子中,咱们首先调用了 foo(1)foo(2)foo(3)foo(4)。此时,因为 foo(1) 的缓存曾经被淘汰,缓存中仅保留了 foo(2)foo(3)foo(4) 的后果。调用 foo.cache_info(),咱们能够看到缓存未命中的次数为 4,以后缓存的使用量为 3。

而后咱们再次调用 foo(4),因为这个后果曾经在缓存中,所以这次是缓存命中,调用 foo.cache_info(),咱们能够看到缓存命中的次数变成了 1。

最初,咱们调用 foo.cache_clear() 清空了所有的缓存,再次调用 foo.cache_info(),咱们能够看到以后缓存的使用量变成了 0。

以上,咱们介绍了 functools.lru_cache 装璜器的应用办法和原理,包含如何应用 lru_cache 对函数进行优化,以及如何清理和查看缓存。心愿这篇文章可能帮忙你更好地了解和应用 functools.lru_cache

正文完
 0