HOWTO · Python
Python 中的运算顺序
本教程讨论 Python 中运算符的执行顺序,涵盖运算符优先级、算术运算符、逻辑运算符,以及使用括号使意图更清晰。扎实理解 Python 如何求值表达式,从而编写更好的代码并避免常见陷阱。
本页内容
Python 按照优先级求值运算符,因此乘法和除法的结合比加法和减法更紧密。要预测结果,请先按优先级将表达式分组,然后考虑结合性以及任何短路逻辑运算符。括号使预期的分组变得明确。
什么是运算符优先级?
运算符优先级是 Python 用来对表达式的各部分进行分组的优先顺序。它描述的是分组,而不是一个通用的从左到右执行顺序:结合性决定具有相同优先级的运算符如何分组,Python 按其指定的顺序求值操作数。例如,3 + 4 * 2 分组为 3 + (4 * 2),值为 11。
以下参考列出了常见的 Python 运算符,从高优先级到低优先级。同一行中的运算符共享一个优先级级别;请使用结合性规则或括号来确定它们的分组。
| 优先级,从高到低 | 运算符或表达式 |
|---|---|
| 分组与访问 | (expressions), x[index], x(...), x.attribute |
| Await | await x |
| 幂运算 | ** |
| 一元数值与位运算 | +x, -x, ~x |
| 乘法 | *, @, /, //, % |
| 加法 | +, - |
| 位移 | <<, >> |
| 位与 AND | & |
| 位异或 XOR | ^ |
| 位或 OR | ` |
| 比较与成员/身份测试 | <, <=, >, >=, !=, ==, in, not in, is, is not |
| 布尔 NOT | not x |
| 布尔 AND | and |
| 布尔 OR | or |
| 条件表达式 | x if condition else y |
| Lambda | lambda |
| 赋值表达式 | := |
这是针对本文所讨论运算符的实用参考,而不是每种表达形式的完整语法。PEMDAS 是一个常见的算术记忆法,但它并未描述所有 Python 运算符,也不能取代上述优先级规则。幂运算还会从右到左分组,因此 2 ** 3 ** 2 意指 2 ** (3 ** 2)。一元正负号与幂运算之间有特殊关系:-2 ** 2 分组为 -(2 ** 2)。
基本算术运算符
在算术中,括号最先分组;其次是幂运算;然后是乘法、除法、整除和取模;最后是加法和减法。乘法和除法具有相同的优先级,并从左到右结合,加法和减法也是如此。/ 运算符执行真正的除法,因此整数相除可以产生浮点数。
运行此示例:
a = 10
b = 5
c = 2
mixed = a + b * c - a / b
explicit = (a + b) * c - a / b
print(mixed)
print(explicit)
第一个表达式分组为 10 + (5 * 2) - (10 / 5);第二个表达式分组为 (10 + 5) * 2 - (10 / 5)。除法产生浮点值,因此最终值为浮点数。此示例尚未执行,因此其输出未在此处显示。
相同的规则可以解释 10 + 2 / 2:除法先被分组,因此值为 11。Python 并不会仅仅因为加法在表达式中先出现就先选择加法。
使用括号使意图更清晰
括号建立分组,并可以改变结果。在上面的例子中,(a + b) 在乘法之前被分组,这改变了与没有括号的表达式相比的算术。当括号可以使您预期的计算更清晰时,请加上它们,即使 Python 的默认优先级已经会产生相同的分组。
括号控制分组;它们并不意味着表达式的每个部分都被急切地求值。Python 仍然遵循每个运算符的语义,包括 and 和 or 的短路行为。
比较与逻辑运算符
==、!=、<、<=、> 和 >= 等比较的优先级高于 not、and 和 or。括号有助于显示哪些比较会输入到逻辑表达式中。例如,(10 > 5) and (15 > 10) 结合了两个为真的比较,因此该表达式为真。
not 适用于其后的表达式,且结合比 and 更紧密;and 的结合比 or 更紧密。and 和 or 都会短路:当左侧操作数决定结果时,Python 可以跳过对右侧操作数的求值。它们还会返回其操作数之一,而不是总是返回布尔值。在条件中,Python 根据其真值来处理结果值。
对于可读的条件,请编写明确的分组,例如 (age >= 18) and (has_ticket or is_guest)。这使预期的关系变得清晰,并有助于防止将优先级规则误认为业务规则。
链式比较
Python 支持诸如 10 > 5 > 15 的比较链。这在结果上等同于 10 > 5 and 5 > 15,中间的表达式只求值一次。第一个比较为真,第二个比较为假,因此整个链为假。Python 并不会将 10 > 5 的布尔结果与 15 进行比较。
链对于范围检查非常有用:0 <= score <= 100 要求两个比较都为真。如果比较涉及不同的表达式,或者中间值应被求值多次,请明确编写预期的比较,并在能提高清晰度的情况下使用临时变量。
结论
要预测 Python 表达式,请应用运算符优先级规则,在运算符共享一个级别时检查结合性,并考虑短路等行为。使用括号来传达您预期的分组。这些习惯使算术和条件表达式更易于阅读和调试。
常见问题
-
Python 中的运算符优先级是什么?
它是 Python 用来对表达式中的运算符进行分组的优先级。结合性和每个运算符的语义也会影响表达式的求值方式。
-
如何更改表达式的分组?
在应最先分组的子表达式周围加上括号,例如
(a + b) * c。 -
当运算符具有相同优先级时会发生什么?
这取决于运算符。大多数运算符从左到右分组,幂运算从右到左分组,比较可以形成链。当分组可能不明确时,请添加括号。
-
如果我在复杂表达式中省略括号会怎样?
Python 会应用其定义的优先级和结合性规则。如果该分组难以看清,请添加括号以使预期的计算变得明确。
-
我可以链接比较运算符吗?
可以。像
0 <= score <= 100这样的链会检查这两个比较,并对中间的表达式求值一次。