Python `or`、`and` 运算符优先级示例
Posted
技术标签:
【中文标题】Python `or`、`and` 运算符优先级示例【英文标题】:Python `or`, `and` operator precedence example 【发布时间】:2019-01-17 22:07:13 【问题描述】:我无法在 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
时,对于 4 种可能性中的每一种,它都是唯一评估的表达式。如果运算符优先级有效,则应打印 "nay\nnope\nyay\nTrue"
或 "nay\nyay\nTrue"
,并进行短路,因为应首先评估 and
。
从这个例子中想到的是,Python 从左到右读取布尔表达式,并在结果已知时结束它,而不管运算符优先级如何。
我的错误在哪里或我错过了什么?请举一个例子,其中and
被评估为第一个,这不是因为代码从左到右解释。
【问题讨论】:
请注意,运算符优先级与评估顺序不同。 【参考方案1】:实际上,您的代码返回 1
并不是因为 or
被第二次评估,而是因为 1 为真,无需进一步评估。这使得行为一致。
【讨论】:
【参考方案2】:您混淆了运算符优先级和评估顺序。
表达式r = x or y and z
的计算结果不是tmp = y and z; r = x or tmp
,而是r = x or (y and z)
。这个表达式是从左到右求值的,如果or
的结果已经决定了,那么(y and z)
根本不会求值。
请注意,如果or
和and
是函数,则情况会有所不同;在这种情况下,将在调用函数本身之前评估函数的参数。因此,operator.or_(yay(), operator.and_(nay(), nope()))
打印 yay
、nay
和 nope
,即打印所有三个,但仍按从左到右的顺序。
您也可以将此推广到其他运算符。由于运算符优先级不同(使用(...)
隐式和显式),以下两个表达式会产生不同的结果,但函数都是从左到右调用的。
>>> 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
正如 cmets 中所指出的,虽然操作之间的项是从左到右评估的,但实际操作是根据它们的优先级评估的。
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)
被计算为1
、2
、3
、pow(2,3)
、4
、mul(8,4)
、add(1,32)
,即从左到右计算项正确(并压入堆栈),表达式在评估其参数后立即评估。
【讨论】:
我知道所有参数在传递给函数之前都必须进行评估,但是您使用operator.or_
和 operator.and_
的示例是一个有趣的反例,并表明 and
和 or
运算符评估不能使用来自operator
的函数进行复制。所以运算符优先级仅与隐式括号嵌套有关,而不与评估顺序有关吗?
在你的例子中,函数都是按从左到右的顺序调用的,但是运算符的计算是按优先顺序进行的:所以定义一个实现算术运算符并打印它们的类,你会看到计算是不是从左到右。事实上,如果你这样做,你甚至会看到 f(1) ** f(2) ** f(3)
从右到左调用 __pow__
方法。
@Duncan 是的,操作是按优先级评估的,我并不是要否认这一点。希望我的编辑可以清除它。
有人可能会争辩说,从右到左的评估不会在这里发生,但它看起来只是因为当调用运算符时表达式的数量越来越少,因此行变得越来越短。你不是故意回去的,这就像逆着自动扶梯的方向。【参考方案3】:
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
条件并且第一部分已经是 False
) - 所以我们不运行。然后我们运行yay()
,因为它可以改变表达式的已经False
的值——返回True
。
【讨论】:
这是一个很好的短电路示例,但它没有显示运算符优先级和评估顺序之间的差异。这就是为什么我要求and
,因为从左到右的评估顺序,它没有被评估为第一个。【参考方案4】:
or
的优先级低于and
,因此a or b and c
被解释为a or (b and c)
。
此外,逻辑运算符是“惰性”评估的,因此如果a
是True
,a or b
将不会导致b
的评估。
【讨论】:
【参考方案5】:Python 从左到右计算表达式,并在知道结果后立即停止。 例如,在 or 运算符的情况下,如果左侧的实体为 True,则可以肯定该运算符将返回 true,但在这种情况下不会计算右侧的表达式。
在and
运算符的情况下,如果左侧的表达式为False,则该运算符肯定返回False。所以这里不对右边的表达式求值。
这就是您的示例中发生的情况。
【讨论】:
它不会从左到右评估所有内容:a if cond else b
将在 a
或 b
之前评估 cond
。
这里的语法我没看懂。你能解释一下吗。我有点想知道你想说什么。
我只是说,虽然 Python 中的大多数表达式从左到右求值,但也有例外。条件表达式计算条件,然后计算左或右表达式之一,因此当条件为真时,它不能遵循从左到右的规则。见docs.python.org/3/reference/…
感谢您告诉我。以上是关于Python `or`、`and` 运算符优先级示例的主要内容,如果未能解决你的问题,请参考以下文章