Python 运算符优先级

9 投票
3 回答
5357 浏览
提问于 2025-04-16 01:48

Python的文档说,*(乘法)和/(除法)是同级别的运算符。
我知道在Python中,表达式是从左到右计算的。

那么我可以依靠这个规则,假设j*j/m总是等于(j*j)/m,也就是可以不加括号吗?
如果是这样的话,我能否认为对于同级别的运算符,这个规则都是适用的呢?


补充说明:这个问题对我来说是合适的,
我在阅读只使用整数的代码(像上面的例子)时,发现没有括号,这让我当时觉得很可疑。

3 个回答

3

简短的回答是:是的。

Python的文档里说了以下内容:

同一类的运算符优先级是一样的。除非特别说明,否则运算符都是二元的。同一类的运算符从左到右进行分组(比较运算符除外,包括测试,这些运算符优先级相同,并且从左到右链式连接……而指数运算符则是从右到左分组)。

换句话说,你的问题的答案是肯定的,优先级相同的运算符会从左到右分组,除了比较运算符,它们是链式连接而不是分组

>>> x = 0
>>> y = 0
>>> x == y == True
False
>>> (x == y) == True
True
>>> x == (y == True)
True

还有指数运算:

>>> 2 ** 2 ** 3
256
>>> (2 ** 2) ** 3
64
>>> 2 ** (2 ** 3)
256

另外,在赋值时,右边的表达式会先计算,然后再计算左边的:

>>> x = 1
>>> y = x = 2
>>> y
2
14

但是,如果对你这个编码者来说有些模糊,那肯定是因为你在问问题,所以可以预期读代码的人也会感到模糊。为了让他们更清楚,最好多花几个字节来解释一下。

依赖优先级规则对于编译器来说是不错的,但对普通人来说就不一定了。

对评论的补充回应

对于那些在阅读代码时遇到模糊情况,需要查阅外部资料来确认的人,你应该假设下一个读者的水平可能不如你,因此最好为他们添加括号,省去他们的麻烦,避免不必要的错误。

实际上,连被接受的答案都有错误(在理由上,而不是结果上,见它的第一条评论),我之前并不知道,很多点赞的人也不知道。

关于基础代数的说法,原帖中用的例子很有启发性。不管运算符的优先级如何,表达式 j * (j / m) 在代数上和 (j * j) / m 是相同的。不幸的是,Python 的代数只是“理想代数”的一种近似,这可能会导致根据 jm 的大小,任一形式都可能得出错误的结果。例如:

>>> m = 1e306
>>> m
1e+306
>>> j = 1e307
>>> j
9.9999999999999999e+306
>>> j / m
10.0
>>> j*j
inf
>>> j * (j / m)
1e+308
>>> (j * j) / m
inf
>>> ((j * j) / m) == (j * (j/m))
False

所以,实际上 Python(以及我的浮点运算单元)的准代数的恒等性质并不成立。而且在你的机器上可能会有所不同,因为文档中提到

浮点数在 C 语言中是用双精度实现的。除非你知道自己正在使用的机器,否则它们的精度就无法保证。

可以说,处理溢出边缘的事情不太合适,这在某种程度上是对的,但如果脱离上下文,表达式在一种运算顺序下是未确定的,而在另一种运算顺序下又是“正确”的。

14

没错,优先级相同的运算符是从左到右进行计算的,也就是说,最左边的两个项会先进行运算,然后把结果和第三个项再运算,以此类推。

不过有一个例外,就是 ** 这个运算符:

>>> 2 ** 2 ** 3
256

另外,比较运算符(比如 ==> 等等)并不是按照关联性来运算的,而是把 x [cmp] y [cmp] z 转换成 (x [cmp] y) and (y [cmp] z) 这样的形式。

撰写回答