避免或延迟评估可能未使用的东西

时间:2012-03-21 10:45:41

标签: python lazy-evaluation

如何在Python中实现延迟评估?几个简单的例子:

>>> def foo(x):
...     print(x)
...     return x
... 
>>> random.choice((foo('spam'), foo('eggs')))
spam
eggs
'eggs'

上面,我们并不需要评估这个元组的所有项目,以便选择一个。在下面,除非查找键实际上从dict中丢失,否则不需要计算默认的foo()

>>> d = {1: "one"}
>>> d.get(2, foo("default"))
default
'default'
>>> d.get(1, foo("default"))
default
'one'

我正在寻找一种Pythonic方法来重构像上面这样的例子,以便懒惰地评估。

4 个答案:

答案 0 :(得分:7)

在Python中进行延迟评估的标准方法是使用generators

def foo(x):
    print x
    yield x

random.choice((foo('spam'), foo('eggs'))).next()

顺便说一句。 Python也允许generator expressions,因此下面的行不会预先计算任何内容:

g = (10**x for x in xrange(100000000))

答案 1 :(得分:5)

您可以使用partial( - 应用函数):

import random
def foo(x):
    print x
    return x

from functools import partial
print random.choice((partial(foo,'spam'), partial(foo,'eggs')))()

当您需要具有默认值的词典时,您可以使用defaultdict

from collections import defaultdict
d = defaultdict(somedefault)
print d[k] # calls somedefault() when the key is missing

Python不是一种懒惰的语言,并且没有特殊的懒惰支持。如果要在以后生成单个值,则必须将其包装在函数中。此外,generators可用于在以后生成一系列值。

答案 2 :(得分:1)

除非你使用更实际的例子,否则我会这样做:

>>> def foo(x):
...     print x
...     return x
...
>>> foo(random.choice(("spam", "eggs")))
spam
'spam'

但你可以创建一个这样的辅助类:

class LazyEval(object):
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def __call__(self):
        return self.func(*self.args, **self.kwargs)

random.choice((LazyEval(foo, "spam"), LazyEval(foo, "eggs")))()

答案 3 :(得分:0)

另一个解决方案是构建并返回一个callable,它封装了你想要对随机选择执行的动作。

def foo(sequence):
    def chooser():
        choice = random.choice(sequence)
        print choice
        return choice
    return chooser

>>> c = foo(['spam', 'eggs', 'ham'])
>>> c()
... ham
>>> 'ham'