Python`or`,`and`运算符优先级示例

时间:2018-08-10 10:02:39

标签: python boolean-logic operator-precedence short-circuiting

我无法在Python中产生显示布尔运算符优先级规则与短路评估相结合的示例。我可以使用以下方式显示运算符优先级:

print(1 or 0 and 0)  # Returns 1 because `or` is evaluated 2nd.

但是当我将其更改为此时,就会出现短路问题:

def yay(): print('yay'); return True
def nay(): print('nay')
def nope(): print('nope')
print(yay() or nay() and nope())  # Prints "yay\nTrue"

对于or之前的表达式为True的四种可能性中的每一种,它都是唯一的求值表达式。如果运算符优先级可行,则应该打印"nay\nnope\nyay\nTrue""nay\nyay\nTrue"并短路,因为and应该被评估为第一。

从此示例中想到的是,Python从左到右读取布尔表达式,并在知道结果的情况下结束布尔表达式,而与运算符的优先级无关。

我的错误在哪里或我错过了什么?请举一个示例,可以看出and是第1个值,而不是因为代码是从左向右解释的。

5 个答案:

答案 0 :(得分:5)

您正在混淆运算符的优先级和评估顺序。

表达式r = x or y and z的求值方式不是tmp = y and z; r = x or tmp,而是r = x or (y and z)。该表达式是从左到右求值的,如果or的结果已经确定,那么(y and z)根本就不会求值。

请注意,如果orand是函数,则会有所不同;在这种情况下,将在调用函数本身之前先评估函数的参数。因此,operator.or_(yay(), operator.and_(nay(), nope()))打印yaynaynope,即,它打印全部三个,但仍然从左到右顺序。

您也可以将其推广到其他运算符。由于运算符优先级不同(使用(...)隐式和显式),以下两个表达式将产生不同的结果,但是两次都从左到右调用了函数。

>>> def f(x): print(x); return x
>>> f(1) + f(2) * f(3) / f(4) ** f(5) - f(6)         # 1 2 3 4 5 6 -> -4.99
>>> (f(1) + f(2)) * (((f(3) / f(4)) ** f(5)) - f(6)) # 1 2 3 4 5 6 -> -17.29

如评论中所指出的,虽然操作之间的术语是从左到右评估的,但实际操作是根据它们的优先级评估的。

class F:
    def __init__(self,x): self.x = x
    def __add__(self, other): print(f"add({self},{other})"); return F(self.x+other.x)
    def __mul__(self, other): print(f"mul({self},{other})"); return F(self.x*other.x)
    def __pow__(self, other): print(f"pow({self},{other})"); return F(self.x**other.x)
    def __repr__(self): return str(self.x)
def f(x): print(x); return F(x)

这样,表达式f(1) + f(2) ** f(3) * f(4)的计算方式为123pow(2,3)4,{{1} },mul(8,4),即术语从左到右求值(并压入堆栈),表达式一经求值就立即求值。

答案 1 :(得分:2)

yay()返回的第一个值是True,因此python甚至不会运行表达式的其余部分。这是为了提高效率,因为表达式的其余部分不会影响结果。

在下面的示例中,我更改了顺序:

def yay(): print('yay'); return True
def nay(): print('nay')
def nope(): print('nope')
print(nay() and nope() or yay())

输出:

nay
yay
True

这是怎么回事? nay()返回None是虚假的,因此我们已经知道nope()返回什么并不重要(因为它是一个and条件,并且第一部分已经是{{ 1}})-因此我们不运行它。然后我们运行False,因为它可能会更改表达式的yay()值-返回False

答案 2 :(得分:1)

实际上,您的代码返回1并不是因为or被评估为第二,而是因为1为真并且不需要进一步评估。这使行为保持一致。

答案 3 :(得分:1)

or的优先级低于and,因此a or b and c被解释为a or (b and c)

此外,逻辑运算符的计算是“延迟”的,因此,如果aTrue,则a or b不会导致对b的求值。

答案 4 :(得分:1)

Python从左到右评估表达式,并在知道结果后立即停止。 例如,对于or运算符,如果左侧的实体为True,则可以确定运算符将返回true,这样在这种情况下将不会对右侧的表达式求值。

对于and运算符,如果左侧的表达式为False,则确保该运算符应返回False。因此,此处不评估右侧的表达式。

这就是您的示例中发生的事情。