Python惰性计算器
是否有一种Pythonic方式来封装惰性函数调用,即在首次使用函数Python惰性计算器,python,caching,lazy-evaluation,memoization,Python,Caching,Lazy Evaluation,Memoization,是否有一种Pythonic方式来封装惰性函数调用,即在首次使用函数f()时,它调用先前绑定的函数g(Z),并在后续调用f()时返回缓存值 请注意,回忆录可能并不完美 我有: f = g(Z) if x: return 5 elif y: return f elif z: return h(f) a = f(Z) if x: return 5 elif y: return a elif z: return h(a) 代码可以工作,但我
f()
时,它调用先前绑定的函数g(Z)
,并在后续调用f()
时返回缓存值
请注意,回忆录可能并不完美
我有:
f = g(Z)
if x:
return 5
elif y:
return f
elif z:
return h(f)
a = f(Z)
if x:
return 5
elif y:
return a
elif z:
return h(a)
代码可以工作,但我想重新构造它,以便仅在使用值时调用g(Z)
。我不想更改g(…)
的定义,而且Z
的缓存有点大
编辑:我假设
f
必须是一个函数,但情况可能并非如此。尝试使用此装饰器:
class Memoize:
def __init__ (self, f):
self.f = f
self.mem = {}
def __call__ (self, *args, **kwargs):
if (args, str(kwargs)) in self.mem:
return self.mem[args, str(kwargs)]
else:
tmp = self.f(*args, **kwargs)
self.mem[args, str(kwargs)] = tmp
return tmp
(摘自死链接:/)
(发现于此:亚历克斯·马泰利)
编辑:这里有另一种形式的属性(使用
\uuuu get\uuuuu
)有很多装饰程序用于记忆:
http://code.activestate.com/recipes/498110-memoize-decorator-with-o1-length-limited-lru-cache/
想出一个完全通用的解决方案比你想象的要难。例如,您需要注意不可散列的函数参数,并且需要确保缓存不会增长太大
如果您真的在寻找一个惰性函数调用(只有在需要值时才对函数进行实际计算),您可能会使用生成器来实现这一点
编辑:所以我猜你到底想要的是懒惰的评估。这是一个可能正是您所寻找的图书馆:
您可以使用缓存装饰器,让我们看一个例子
from functools import wraps
class FuncCache(object):
def __init__(self):
self.cache = {}
def __call__(self, func):
@wraps(func)
def callee(*args, **kwargs):
key = (args, str(kwargs))
# see is there already result in cache
if key in self.cache:
result = self.cache.get(key)
else:
result = func(*args, **kwargs)
self.cache[key] = result
return result
return callee
使用缓存装饰器,您可以在这里编写
my_cache = FuncCache()
@my_cache
def foo(n):
"""Expensive calculation
"""
sum = 0
for i in xrange(n):
sum += i
print 'called foo with result', sum
return sum
print foo(10000)
print foo(10000)
print foo(1234)
正如您可以从输出中看到的那样
called foo with result 49995000
49995000
49995000
foo将只被调用一次。您不必更改函数foo的任何一行。这就是装饰师的力量。即使在你编辑之后,以及与detly的一系列评论之后,我仍然不太明白。在第一句话中,您说对f()的第一次调用应该调用g(),但随后返回缓存的值。但是在你的评论中,你说“无论发生什么,g()都不会被调用”(我的重点)。我不知道你在否定什么:你是说永远不应该调用g()(没有什么意义;为什么g()存在?);或者g()可能会被调用,但可能不会(这仍然与第一次调用f()时调用g()相矛盾)。然后给出一个完全不涉及g()的片段,它与问题的第一句话或detly的注释线程都没有关系 如果您再次编辑它,下面是我要回复的代码片段: 我有:
f = g(Z)
if x:
return 5
elif y:
return f
elif z:
return h(f)
a = f(Z)
if x:
return 5
elif y:
return a
elif z:
return h(a)
代码是有效的,但我想
重新构造它,使f(Z)仅为
如果使用了该值,则调用。我不
要更改的定义
f(…),Z缓存起来有点大
如果这真的是你的问题,那么答案很简单
if x:
return 5
elif y:
return f(Z)
elif z:
return h(f(Z))
这就是如何实现“仅当使用该值时才调用f(Z)”
我不完全理解“Z有点大,需要缓存”。如果你的意思是在程序执行的过程中会有太多不同的Z值,那么记忆是无用的,那么你可能不得不求助于预先计算f(Z)的所有值,并在运行时查找它们。如果你不能做到这一点(因为你不知道你的程序将遇到的Z值),那么你就回到记忆化。如果这仍然太慢,那么您唯一的选择就是使用比Python更快的东西(试试Psyco、Cython、ShedSkin或手工编码的C模块)。我有点困惑您是寻求缓存还是延迟计算。对于后者,请查看模块。为了完整起见,这里有一个关于我的懒惰评估器装饰器配方的链接: 是一个相当简单的惰性装饰程序,尽管它缺少使用
@functools.wrapps
(实际上返回了惰性的一个实例以及一些其他潜在缺陷):
我不确定这就是通常所说的懒惰。称之为缓存或记忆更安全。@John Y是对的:“惰性计算”指不计算不会影响包含表达式结果的表达式的结果,例如在f()和g()
中,如果f()
为False
,则惰性计算不会调用g()
。这个问题与此无关。当有函数参数时,它是记忆。否则,这只是一个懒惰的函数调用。@Neil G-肯定是一个缓存的函数结果吗g()
无论如何至少被调用一次。@详细说明了我想要的:g()
无论如何都不会被调用。或者,如果f(Z)
表达式比实际中的表达式长,只需使用两个独立的if
语句,第二个嵌套在第一个的else
子句中。我试图按照我认为的方式编写代码,因此我希望在输入if
之前将f(Z)绑定到某个名称。如果我需要f(Z)的结果,我可以查询它的长度,使用它的值,等等。我知道只有在需要的时候才会创建它。我很确定这正是我想要的。