我刚开始学习Python,我不知道什么是内存化,也不知道如何使用它。另外,我可以举一个简单的例子吗?
当前回答
我发现这非常有用
from functools import wraps
def memoize(function):
memo = {}
@wraps(function)
def wrapper(*args):
# add the new key to dict if it doesn't exist already
if args not in memo:
memo[args] = function(*args)
return memo[args]
return wrapper
@memoize
def fibonacci(n):
if n < 2: return n
return fibonacci(n - 1) + fibonacci(n - 2)
fibonacci(25)
其他回答
functools。缓存装饰:
Python 3.9发布了一个新函数functools.cache。它在内存中缓存带有一组特定参数的函数调用的结果,这就是内存化。它很容易使用:
import functools
import time
@functools.cache
def calculate_double(num):
time.sleep(1) # sleep for 1 second to simulate a slow calculation
return num * 2
第一次调用caculate_double(5)时,它将花费一秒钟并返回10。第二次使用相同的参数calculate_double(5)调用该函数时,它将立即返回10。
添加缓存装饰器可以确保如果函数最近为某个特定值被调用,它将不会重新计算该值,而是使用先前缓存的结果。在这种情况下,它可以极大地提高速度,同时代码不会因为缓存的细节而变得混乱。
(编辑:前面的示例使用递归计算了斐波那契数,但我修改了示例以防止混淆,因此出现了旧的注释。)
functools。lru_cache装饰:
如果您需要支持旧版本的Python,请使用functools。lru_cache适用于Python 3.2+。默认情况下,它只缓存最近使用的128个调用,但你可以将maxsize设置为None来指示缓存永远不会过期:
@functools.lru_cache(maxsize=None)
def calculate_double(num):
# etc
记忆实际上是指根据方法输入记住(“记忆”→“备忘录”→被记住)方法调用的结果,然后返回记住的结果,而不是重新计算结果。您可以把它看作是方法结果的缓存。更多详细信息,请参阅第387页的算法介绍(3e), Cormen等人的定义。
在Python中使用内存计算阶乘的简单示例如下:
factorial_memo = {}
def factorial(k):
if k < 2: return 1
if k not in factorial_memo:
factorial_memo[k] = k * factorial(k-1)
return factorial_memo[k]
你可以做得更复杂一些,把记忆过程封装到一个类中:
class Memoize:
def __init__(self, f):
self.f = f
self.memo = {}
def __call__(self, *args):
if not args in self.memo:
self.memo[args] = self.f(*args)
#Warning: You may wish to do a deepcopy here if returning objects
return self.memo[args]
然后:
def factorial(k):
if k < 2: return 1
return k * factorial(k - 1)
factorial = Memoize(factorial)
Python 2.4中添加了一个被称为“装饰器”的特性,它允许你现在简单地编写以下代码来完成同样的事情:
@Memoize
def factorial(k):
if k < 2: return 1
return k * factorial(k - 1)
Python装饰器库有一个类似的装饰器,称为memoized,它比这里显示的Memoize类稍微健壮一些。
只是想对已经提供的答案进行补充,Python装饰器库有一些简单但有用的实现,也可以记住“不可哈希类型”,不像functools.lru_cache。
我应该先回答第一部分:什么是记忆?
这只是一种用记忆换取时间的方法。想想乘法表。
在Python中使用可变对象作为默认值通常被认为是不好的。但如果明智地使用它,它实际上可以用于实现记忆。
下面是一个摘自http://docs.python.org/2/faq/design.html#why-are-default-values-shared-between-objects的例子
在函数定义中使用可变dict,中间计算结果可以被缓存(例如,在计算阶乘(9)后计算阶乘(10)时,我们可以重用所有中间结果)
def factorial(n, _cache={1:1}):
try:
return _cache[n]
except IndexError:
_cache[n] = factorial(n-1)*n
return _cache[n]
如果要考虑速度:
@functools。cache和@functools.lru_cache(maxsize=None)同样快,在我的系统上循环一百万次需要0.122秒(最好运行15次) 全局缓存变量要慢得多,在我的系统上循环一百万次需要0.180秒(最好运行15次) 一个自我。缓存类变量仍然有点慢,在我的系统上循环一百万次需要0.214秒(最好运行15次)
后两者的实现方式与目前投票最多的答案中描述的类似。
这没有防止内存耗尽,也就是说,我没有在类或全局方法中添加代码来限制缓存的大小,这真的是最基本的实现。如果需要的话,lru_cache方法可以免费提供。
对我来说,一个悬而未决的问题是如何对具有functools装饰器的东西进行单元测试。是否有可能以某种方式清空缓存?单元测试似乎使用class方法(在这里您可以为每个测试实例化一个新类)或全局变量方法(因为您可以使用您的rimportedmodule)是最干净的。Cachevariable ={}来清空它)。
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用pip3安装包时,“Python中的ssl模块不可用”
- 无法切换Python与pyenv
- Python if not == vs if !=
- 如何从scikit-learn决策树中提取决策规则?
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- 将旋转的xtick标签与各自的xtick对齐
- 为什么元组可以包含可变项?
- 如何合并字典的字典?
- 如何创建类属性?
- 不区分大小写的“in”
- 在Python中获取迭代器中的元素个数
- 解析日期字符串并更改格式
- 使用try和。Python中的if
- 如何在Python中获得所有直接子目录