是否有Pythonic方法来封装惰性函数调用,在第一次使用函数f()
时,它调用先前绑定的函数g(Z)
并在连续调用f()
上返回一个缓存的值?
请注意,记忆可能不太合适。
我有:
f = g(Z)
if x:
return 5
elif y:
return f
elif z:
return h(f)
代码可以工作,但我想对其进行重组,以便仅在使用该值时调用g(Z)
。我不想更改g(...)
的定义,而Z
要缓存一点。
编辑:我认为f
必须是一个函数,但情况可能并非如此。
答案 0 :(得分:6)
无论你是寻求缓存还是懒惰评估,我都有点困惑。对于后者,请查看模块lazy.py by Alberto Bertogli。
答案 1 :(得分:3)
尝试使用这个装饰器:
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
(摘自死链接:http://snippets.dzone.com/posts/show/4840 / https://web.archive.org/web/20081026130601/http://snippets.dzone.com/posts/show/4840) (在此处找到:Alex Martelli Is there a decorator to simply cache function return values?)
编辑:这是另一种属性形式(使用__get__
)http://code.activestate.com/recipes/363602/
答案 2 :(得分:1)
有很多装饰者在那里进行记忆:
http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize http://code.activestate.com/recipes/498110-memoize-decorator-with-o1-length-limited-lru-cache/ http://code.activestate.com/recipes/496879-memoize-decorator-function-with-cache-size-limit/
提出一个完全通用的解决方案比你想象的更难。例如,您需要注意不可散列的函数参数,并且需要确保缓存不会变得太大。
如果你真的在寻找一个惰性函数调用(只有在需要值时才会实际评估该函数),你可能会使用生成器。
编辑:所以我猜你想要的毕竟是懒惰的评价。这是一个可能正在寻找的图书馆:
答案 3 :(得分:1)
您可以使用缓存装饰器,让我们看一个例子
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的任何行。这就是装饰者的力量。
答案 4 :(得分:1)
为了完整起见,这里是我的懒惰评估装饰师食谱的链接:
https://bitbucket.org/jsbueno/metapython/src/f48d6bd388fd/lazy_decorator.py
答案 5 :(得分:1)
Here是一个非常简短的懒惰装饰者,虽然它没有使用@functools.wraps
(实际上返回Lazy
的实例加上一些其他潜在的陷阱):
class Lazy(object):
def __init__(self, calculate_function):
self._calculate = calculate_function
def __get__(self, obj, _=None):
if obj is None:
return self
value = self._calculate(obj)
setattr(obj, self._calculate.func_name, value)
return value
# Sample use:
class SomeClass(object):
@Lazy
def someprop(self):
print 'Actually calculating value'
return 13
o = SomeClass()
o.someprop
o.someprop
答案 6 :(得分:0)
即使你的编辑,以及一系列的评论与我有点,我仍然不太明白。在你的第一句话中,你说第一次调用f()应该调用g(),但随后返回缓存的值。但是在你的评论中,你说“g()不会被调用,无论什么”(强调我的)。我不确定你在否定:你是说g()应该从不被调用(没有多大意义;为什么g()存在?);或者g()可能被调用,但可能不会(嗯,这仍然与第一次调用f()时调用g()相矛盾)。然后你给出一个完全不涉及g()的片段,并且实际上与你的问题的第一句话或者与评论线程无关。
如果你再次编辑它,这是我回复的片段:
我有:
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值,那么memoization就没用了,那么你可能需要预先计算f(Z)的所有值并在运行时查找它们。如果你不能这样做(因为你无法知道你的程序会遇到Z的值),那么你又回到了memoization。如果那仍然太慢,那么你唯一真正的选择是使用比Python更快的东西(尝试Psyco,Cython,ShedSkin或手工编码的C模块)。