Python中的延迟求值
我听说过Python中的延迟求值(deferred evaluation),比如在这里提到的。这个概念是指,解释器只有在使用lambda表达式时才会对它进行计算吗?还是说这是一个正确的术语,用来描述由于Python的动态设计,很多错误直到运行时才会被发现?
或者我是不是完全理解错了什么?
2 个回答
延迟计算就是一个表达式在需要的时候才被计算。在大多数编程语言中,你可以使用类似于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中,默认情况下所有的计算都是延迟的,你不需要特别的语法来实现(不过有特殊的语法可以关闭这个功能)。
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.contents
,stuff
必须是某种具有contents
属性的类型。而在Python中,由于if
语句中的延迟计算,这个条件不一定成立。stuff
可以是None
,显然它没有任何属性,解释器会直接执行else
部分,而不会执行前面的部分。因此,这在Python中是有效的,甚至是一种习惯用法,使得代码更简单。