Python中的延迟求值

9 投票
2 回答
10097 浏览
提问于 2025-04-15 19:28

我听说过Python中的延迟求值(deferred evaluation),比如在这里提到的。这个概念是指,解释器只有在使用lambda表达式时才会对它进行计算吗?还是说这是一个正确的术语,用来描述由于Python的动态设计,很多错误直到运行时才会被发现?

或者我是不是完全理解错了什么?

2 个回答

18

延迟计算就是一个表达式在需要的时候才被计算。在大多数编程语言中,你可以使用类似于lambda的东西来实现这个功能。下面是一个简单的例子,帮助你理解这个概念:

def list_files():
    for fn in os.listdir('.'):
        yield fn, lambda: open(fn, 'r').read()


for fn, body in list_files():
    if fn.endswith('.txt'):
        print body()

在这个例子中,list_files函数返回一堆文件名和一个“惰性计算”(没有参数的lambda),这个惰性计算会返回文件的内容。这个“惰性计算”就是延迟计算的体现。使用惰性计算可以让你把不同的任务分开:

  • for循环不需要知道怎么读取文件,所以list_files可以换成list_ftp_files或者list_zip_archive
  • list_files函数不需要知道具体会读取哪些文件。通过惰性计算,它不需要一次性读取所有文件。

在真正的延迟计算中,一旦你计算了这个“惰性计算”,它会用计算后的结果替代自己,所以计算两次的工作量和计算一次是一样的。还有其他方法可以实现同样的效果,比如使用类和对象来缓存值。

延迟计算在Scheme语言中是一个(相对)常见的用法。在Haskell中,默认情况下所有的计算都是延迟的,你不需要特别的语法来实现(不过有特殊的语法可以关闭这个功能)。

8

Dietrich的回答很好,但我想补充一点,最简单的延迟计算形式就是if语句:

if True:
  x = 5
else:
  x = y    # huh? what is y?

这段代码可以正确解析和运行,虽然else部分没有意义——因为y是未定义的。else部分只是被解析,所以从语法上来说在Python中是有效的。这实际上可以用于一些简单的代码:

if stuff:
   print stuff.contents
else:
   print "no stuff"

在强类型语言中,这种写法是行不通的,因为要使用stuff.contentsstuff必须是某种具有contents属性的类型。而在Python中,由于if语句中的延迟计算,这个条件不一定成立。stuff可以是None,显然它没有任何属性,解释器会直接执行else部分,而不会执行前面的部分。因此,这在Python中是有效的,甚至是一种习惯用法,使得代码更简单。

参考讨论

撰写回答