Python是否惰性地评估if语句的条件?

86

例如,如果我有以下陈述:

if( foo1 or foo2)
    ...
    ...
如果foo1为真,Python会检查foo2的条件吗?

请参阅http://docs.python.org/2/reference/expressions.html#boolean-operations。 - Martijn Pieters
11
如果像我一样懒得加载官方文档,那么我的关键字技巧通常是:在解释器控制台中键入help("or")。 在这种情况下,请阅读第四段。 - DSM
3
Python在这里的行为与“if”无关,而与“or”有关。 - jwodder
有一些Python模块实现了惰性求值,这可能是你正在寻找的。参考链接 - Anderson Green
从技术上讲,如果布尔运算符稍后用于实际布尔运算,则Python会短路然后双重评估结果...除非它直接位于“if”语句中...这比not或bool()更为重要,因此会评估一次。双重评估取决于操作的复杂性。 这是违反直觉的,但在此处有证明:https://gist.github.com/earonesty/08e9cbe083a5e0583feb8a34cc538010 - Erik Aronesty
7个回答

114

是的,Python对布尔条件进行惰性求值。

文档说:

表达式 x and y 首先对 x 进行求值;如果 x 为 false,则返回其值;否则,对 y 进行求值并返回结果值。

表达式 x or y 首先对 x 进行求值;如果 x 为 true,则返回其值;否则,对 y 进行求值并返回结果值。


5
我想指出,也可以进行比特位比较,这种方法不像下面Eric Wang的回答那样是懒惰的。你也可以查看文档 - Bruno Silvano

72

andor是惰性求值的。

&|不是惰性求值的。


25

以下代码可以证明Python的惰性:

def foo():
    print('foo')
    return False

def bar():
    print('bar')
    return False

foo() and bar()         #Only 'foo' is printed

另一方面,

foo() or bar()

会导致'foo'和'bar'都被打印出来。


7

这并不是严格的惰性求值,而是短路布尔表达式。

懒惰求值有着稍微不同的内涵。例如,真正的惰性求值可能会允许这样做:

def foo(arg) :
    print "Couldn't care less"

foo([][0])

但是 Python 不会。

Python 在布尔参数方面也很好用。例如,一个 or 条件返回它的第一个“真值”参数或最后一个参数(如果所有参数都是“假值”)。而 and 条件则相反。

因此,“回声参数”布尔值意味着

2 and [] and 1

计算结果为 [],而

[] or 1 or 2

计算结果为 1。


2
惰性求值是一种泛指,也可以指逻辑运算符的短路行为。如果你指的是按需调用,通常称为“按需调用”,因为惰性求值可以有很多含义。 - kqr
1
我不同意。那些说“lazy evaluation”却实际上指的是“short circuit evaluation”的人是错的。“call by need”是“lazy evaluation”的同义词。我同意它更不容易被误用。 - Pete Cacioppi
2
@PeteIsNeat 如果你想成为一个技术规范主义者,“lazy evaluation”是实现“call-by-need”语义的众多方式之一。它们不是同义词,也不是等价的。我是一个描述主义者,所以当人们谈论“懒惰行为”,从上下文中很明显他们在谈论短路时,我并不介意。 :) - kqr
1
我是那个在对话中介绍“惰性求值”的人。我只是试图添加一些额外的背景信息。如果你的意思是“短路求值”,那么说“惰性求值”是具有误导性的,我想确保每个人都理解这一点。至于“惰性求值”是否与“按需调用”同义...维基百科页面看起来对我来说是正确的。 - Pete Cacioppi
事实上,短路布尔运算在不同的编程语言中非常普遍,而通用的惰性求值则非常罕见。因此,最明确的说法是Python不是一种惰性求值语言,但它确实提供了短路布尔表达式。 - Pete Cacioppi
显示剩余4条评论

3
一个简单的演示是比较时间差异:
all(xrange(1,1000000000))

并且
any(xrange(1,1000000000))

all()函数需要检查每一个数值,而any()函数在找到第一个True后就可以停止了。由于xrange是一个生成器,因此当求值器完成时,它也会停止生成东西。因此,all()函数将消耗大量RAM并花费很长时间,而any()函数只使用几个字节并立即返回。


2

是的,Python使用惰性求值,因此不会检查 foo2

如果我不知道键是否存在,我会经常使用这个功能从类似字典的对象中获取项目:

if 'key' in mydict and mydict['key'] == 'heyyo!':
    do_stuff()

请参考 @unutbu 的答案,以获取更详细的解释。

1
对于这种情况,我认为 if mydict.get("key") == 'heyyo!': 也可以工作 - .get 如果找不到键,则会返回 None(或指定的默认值)。 - DSM
正确,是的,所以我通常不用它来处理实际的字典,而用于类似字典的对象(通常来自外部模块),这些对象没有 .get 方法。 - jdotjdot
@DSM实际上针对你的使用情况,我会简单地执行if mydict.get("key"):,因为None会被视为False - jdotjdot
但是这样你就会失去 'heyyo!' 的比较。 - DSM

1

实际上被短路的是or部分:

>>> 1 or 1/0  #also 0 and 1/0
1
>>> 0 or 1/0  #also 1 and 1/0

Traceback (most recent call last):
  File "<pyshell#1240>", line 1, in <module>
    0 or 1/0
ZeroDivisionError: integer division or modulo by zero

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接