我刚开始学习Python,我不知道什么是内存化,也不知道如何使用它。另外,我可以举一个简单的例子吗?
当前回答
下面是一个解决方案,将工作与列表或dict类型参数没有抱怨:
def memoize(fn):
"""returns a memoized version of any function that can be called
with the same list of arguments.
Usage: foo = memoize(foo)"""
def handle_item(x):
if isinstance(x, dict):
return make_tuple(sorted(x.items()))
elif hasattr(x, '__iter__'):
return make_tuple(x)
else:
return x
def make_tuple(L):
return tuple(handle_item(x) for x in L)
def foo(*args, **kwargs):
items_cache = make_tuple(sorted(kwargs.items()))
args_cache = make_tuple(args)
if (args_cache, items_cache) not in foo.past_calls:
foo.past_calls[(args_cache, items_cache)] = fn(*args,**kwargs)
return foo.past_calls[(args_cache, items_cache)]
foo.past_calls = {}
foo.__name__ = 'memoized_' + fn.__name__
return foo
请注意,通过在handle_item中实现您自己的哈希函数,这种方法可以自然地扩展到任何对象。例如,为了使这种方法适用于一个接受set作为输入参数的函数,你可以在handle_item中添加:
if is_instance(x, set):
return make_tuple(sorted(list(x)))
其他回答
不要忘记内置的hasattr函数,对于那些想要手工制作的人来说。这样就可以将mem缓存保存在函数定义中(而不是全局缓存)。
def fact(n):
if not hasattr(fact, 'mem'):
fact.mem = {1: 1}
if not n in fact.mem:
fact.mem[n] = n * fact(n - 1)
return fact.mem[n]
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
我应该先回答第一部分:什么是记忆?
这只是一种用记忆换取时间的方法。想想乘法表。
在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]
与传递关键字参数的顺序无关的位置参数和关键字参数的解决方案(使用inspect.getargspec):
import inspect
import functools
def memoize(fn):
cache = fn.cache = {}
@functools.wraps(fn)
def memoizer(*args, **kwargs):
kwargs.update(dict(zip(inspect.getargspec(fn).args, args)))
key = tuple(kwargs.get(k, None) for k in inspect.getargspec(fn).args)
if key not in cache:
cache[key] = fn(**kwargs)
return cache[key]
return memoizer
类似的问题:在Python中识别用于内存化的等效可变参数函数调用
如果要考虑速度:
@functools。cache和@functools.lru_cache(maxsize=None)同样快,在我的系统上循环一百万次需要0.122秒(最好运行15次) 全局缓存变量要慢得多,在我的系统上循环一百万次需要0.180秒(最好运行15次) 一个自我。缓存类变量仍然有点慢,在我的系统上循环一百万次需要0.214秒(最好运行15次)
后两者的实现方式与目前投票最多的答案中描述的类似。
这没有防止内存耗尽,也就是说,我没有在类或全局方法中添加代码来限制缓存的大小,这真的是最基本的实现。如果需要的话,lru_cache方法可以免费提供。
对我来说,一个悬而未决的问题是如何对具有functools装饰器的东西进行单元测试。是否有可能以某种方式清空缓存?单元测试似乎使用class方法(在这里您可以为每个测试实例化一个新类)或全局变量方法(因为您可以使用您的rimportedmodule)是最干净的。Cachevariable ={}来清空它)。
推荐文章
- Numpy在这里函数多个条件
- 在Python中,使用argparse只允许正整数
- 如何排序mongodb与pymongo
- 不可变与可变类型
- 列表是线程安全的吗?
- 操作系统。makdirs在我的路径上不理解“~”
- 如何在Django模板中获得我的网站的域名?
- 在django Forms中定义css类
- 如何在Python中scp ?
- Numpy Max vs amax vs maximum
- 我应该在.gitignore文件中添加Django迁移文件吗?
- 每n行有熊猫
- 实例属性attribute_name定义在__init__之外
- 如何获取在Python中捕获的异常的名称?
- 第一次出现的值大于现有值的Numpy