Python惰性求值器
Posted
技术标签:
【中文标题】Python惰性求值器【英文标题】:Python lazy evaluator 【发布时间】:2011-07-14 18:45:02 【问题描述】:是否有一种 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
必须是一个函数,但事实可能并非如此。
【问题讨论】:
我不确定这就是 lazy 的通常含义。将其称为 caching 或 memoization 更安全。 @John Y 是对的:“惰性求值”是指不计算不会影响包含表达式结果的表达式的结果,例如。在f() and g()
中,如果f()
是False
,则惰性求值不会调用g()
。这个问题与此无关。
当有函数参数时它是记忆。否则,它只是一个惰性函数调用。
@Neil G - 它肯定是 cached 函数结果吗?无论如何,g()
至少会被调用一次。
@detly 澄清了我想要什么:g()
无论如何都不会被调用。
【参考方案1】:
无论您是寻求缓存还是惰性求值,我都感到有些困惑。对于后者,请查看模块 lazy.py by Alberto Bertogli。
【讨论】:
我很确定这正是我想要的。【参考方案2】:尝试使用这个装饰器:
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) (在这里找到:Is there a decorator to simply cache function return values? by Alex Martelli)
编辑:这是另一种形式的属性(使用__get__
)http://code.activestate.com/recipes/363602/
【讨论】:
【参考方案3】:你可以使用缓存装饰器,看一个例子
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】:有很多装饰器用于记忆:
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/
想出一个完全通用的解决方案比您想象的要难。例如,您需要注意不可散列的函数参数,并且需要确保缓存不会变得太大。
如果您真的在寻找惰性函数调用(仅在需要值时才对函数进行实际评估),您可能会为此使用生成器。
编辑:所以我想你真正想要的是懒惰的评估。这可能是您正在寻找的库:
http://pypi.python.org/pypi/lazypy/0.5
【讨论】:
【参考方案5】:为了完整起见,这里是我的惰性评估器装饰器配方的链接:
https://bitbucket.org/jsbueno/metapython/src/f48d6bd388fd/lazy_decorator.py
【讨论】:
【参考方案6】: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
【讨论】:
【参考方案7】:即使经过你的编辑,以及一系列的 cmets,我仍然不太明白。在您的第一句话中,您说第一次调用 f() 应该调用 g(),但随后返回缓存值。但是然后在您的 cmets 中,您说“g() 不无论如何都不会被调用”(强调我的)。我不确定你在否定什么:你是说 g() 应该 never 被调用(没有多大意义;为什么 g() 存在?);或者 g() 可能 被调用,但可能不会(嗯,这仍然与第一次调用 f() 时调用 g() 相矛盾)。然后,您给出一个根本不涉及 g() 的 sn-p,并且实际上与您问题的第一句话或与 detly 的评论线程无关。
如果您再次编辑它,这是我正在回复的 sn-p:
我有:
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 模块)。
【讨论】:
另外,如果f(Z)
表达式比实际中的要长,只需有两个单独的if
语句,第二个嵌套在第一个的else
子句中。
我正在尝试按照我的想法编写代码,因此我希望在输入 if
之前将 f(Z) 绑定到某个名称。然后如果我需要 f(Z) 的结果,我可以查询它的长度,使用它的值等等,我知道它只会在需要时被创建。以上是关于Python惰性求值器的主要内容,如果未能解决你的问题,请参考以下文章