Python运算符优先级表:复杂表达式怎么确定计算顺序

方予
方予
发布时间:2026-11-08 | 5人浏览过
原创

提供完整的Python 3运算符优先级与结合性对照表,详解复杂表达式确定计算顺序的三大核心规则(优先级、结合方向、操作数左到右求值与短路机制),拆解高频易错陷阱并给出AST语法树验证方法。

假设你在排查一段订单风控或优惠计算的Python代码时,遇到这样一行没有括号的表达式:not is_blocked and points // 10 + 5 * 2 ** 2 > 25 or flag & 1 == 1。当计算结果与预期不符时,靠直觉从左往右读往往会找错问题位置。在Python中,确定复杂表达式的计算顺序只需要依次对照三件事:先按运算符优先级表把高优先级运算绑定成子表达式,同级运算符按结合方向(左结合、右结合或链式展开)归组,最后按从左到右的操作数求值顺序与短路规则执行计算。本文将给出完整的Python 3运算符优先级对照表,并配合分步推导、高频易错场景和ast标准库验证方法,帮你准确判断任意复杂表达式的执行顺序。

一、Python 3完整运算符优先级与结合性对照表

在Python 3官方语言参考手册(Python Language Reference)中,所有运算符按照绑定紧密程度分为18个等级。表格中位置越靠前(等级数字越小),优先级越高,越先被绑定计算;同一行内的运算符具有完全相同的优先级。

优先级等级 运算符 / 表达式形式 含义说明 结合方向 典型示例与等价括号形式
1(最高) (expr...)、[expr...]、{k: v...}、{expr...} 圆括号分组、列表/字典/集合推导式与字面量 从左到右 (a + b) * c 强制先算 a + b
2 x[index]、x[i:j]、x(args...)、x.attr 下标索引、切片、函数/方法调用、属性引用 从左到右 obj.items()[0] 等价于 (obj.items())[0]
3 await x 协程等待表达式(Python 3.5+) 单目(前缀) await client.get() 等价于 await (client.get())
4 ** 幂运算(乘方) 从右到左 2 ** 3 ** 2 等价于 2 ** (3 ** 2) = 512
5 +x、-x、~x 一元正号、一元负号(取反)、按位取反 从右到左(单目) -3 ** 2 等价于 -(3 ** 2) = -9
6 *、@、/、//、% 乘法、矩阵乘法、浮点除法、向下取整除、取模 从左到右 20 // 3 * 2 等价于 (20 // 3) * 2 = 12
7 +、- 加法、减法 从左到右 10 - 3 + 2 等价于 (10 - 3) + 2 = 9
8 <<、>> 按位左移、按位右移 从左到右 1 << 2 + 1 等价于 1 << (2 + 1) = 8
9 & 按位与(集合交集) 从左到右 6 & 3 + 1 等价于 6 & (3 + 1) = 4
10 ^ 按位异或(集合对称差集) 从左到右 5 ^ 3 & 1 等价于 5 ^ (3 & 1) = 4
11 | 按位或(集合并集 / Python 3.9+字典合并) 从左到右 4 | 2 ^ 3 等价于 4 | (2 ^ 3) = 5
12 in、not in、is、is not、<、<=、>、>=、!=、== 成员检测、身份检测、大小与相等比较 链式展开 a < b <= c 等价于 (a < b) and (b <= c)
13 not x 布尔逻辑“非” 从右到左(单目) not a == b 等价于 not (a == b)
14 and 布尔逻辑“与”(短路求值) 从左到右 a or b and c 等价于 a or (b and c)
15 or 布尔逻辑“或”(短路求值) 从左到右 a and b or c 等价于 (a and b) or c
16 x if c else y 条件表达式(三元运算符) 从右到左 a if c1 else b if c2 else d 等价于 a if c1 else (b if c2 else d)
17 lambda args: expr 匿名函数定义 无(整体绑定右侧) lambda x: x + 1 将 x + 1 作为函数体
18(最低) := 赋值表达式(海象运算符,Python 3.8+) 从右到左 (n := len(a)) > 5 必须加括号,否则 n 会绑定布尔值
记忆分层规律:日常无需死记18个编号,只要把握住7个核心梯队即可:括号与调用索引 > 算术运算(幂 > 正负号 > 乘除 > 加减) > 位运算(移位 > 与& > 异或^ > 或|) > 比较与身份/成员测试 > 逻辑运算(not > and > or) > 三元条件与lambda > 海象赋值:=。
Python运算符优先级表:复杂表达式怎么确定计算顺序
图1:Python 3 运算符优先级七大核心梯队与同层细分顺序

二、确定复杂表达式计算顺序的三大核心规则

拿来一张优先级表,并不等于能直接算出所有复杂表达式的结果。在Python解释器内部,“谁和谁先绑在一起(语法结构)”与“哪个子表达式先执行(运行时求值)”分别由以下三条规则共同决定。

1. 不同等级看优先级:高优先级先聚合成子单元

当一个表达式里同时出现不同等级的运算符时,优先级高的运算符会率先把它左右两侧的操作数“抓取”过来,相当于在它们外层自动套上一对隐式圆括号。例如在 a + b * c ** d 中,**(等级4)高于 *(等级6),* 又高于 +(等级7),因此解释器生成的语法结构必然是 a + (b * (c ** d))。

2. 相同等级看结合性:左结合、右结合与链式比较

当相邻的两个运算符处于同一优先级等级时,谁先绑定取决于该等级的“结合方向”:

  • 从左到右结合(左结合,绝大多数二元运算符):加减(+、-)、乘除取模(*、/、//、%)、位运算、逻辑与(and)、逻辑或(or)均采用左结合。例如 20 // 3 * 2 中,// 和 * 同属等级6,按从左到右结合为 (20 // 3) * 2,先算 20 // 3 得 6,再算 6 * 2 得 12。
  • 从右到左结合(右结合,注意幂运算与条件表达式):二元运算符中,幂运算 **、三元条件表达式 x if c else y 和海象运算符 := 采用右结合;所有单目(前缀)运算符如 -x、~x、not x 自然也是从右向左包裹。例如 2 ** 3 ** 2 会从右侧先绑定为 2 ** (3 ** 2),结果是 2 ** 9 = 512,而不是 (2 ** 3) ** 2 = 64。
  • 链式比较展开(等级12专属机制):所有比较运算符(<、>、==、!=、in、is 等)优先级完全相同。当它们连续出现时,既不是单纯的左结合也不是右结合,而是被展开为用 and 连接的多个比较,且中间公共表达式只计算一次。例如 a < f(x) <= b 等价于先计算一次 temp = f(x),再判断 (a < temp) and (temp <= b)。

3. 操作数求值顺序:从左到右执行与短路跳出

很多人容易把“运算符优先级高”误解为“整行代码里那一部分最先被CPU执行”。实际上,优先级只决定语法树怎么画括号,而在真正执行时,Python总是从左到右依次对各个操作数求值(赋值语句除外,赋值语句先对等号右侧求值)。

比如表达式 f1() + f2() * f3():虽然乘法 * 优先级高于加法 +(结构等价于 f1() + (f2() * f3())),但Python运行时会严格按照从左往右的顺序依次调用 f1()、f2()、f3(),拿到三个返回值后,再先做乘法、后做加法。

唯一会打断“全部从左到右求值”的是短路求值(Short-circuit evaluation):

  • x and y:先计算左操作数 x;若 x 的布尔值为假(如 False、0、""、None、[]),直接返回 x 的原值,完全不计算 y;否则计算并返回 y 的原值。
  • x or y:先计算左操作数 x;若 x 的布尔值为真,直接返回 x 的原值,完全不计算 y;否则计算并返回 y 的原值。
  • x if c else y:先计算中间的条件 c;若 c 为真则只计算并返回 x,若 c 为假则只计算并返回 y。

三、复杂表达式怎么一步步确定计算顺序(实战拆解)

面对一个包含算术、位运算、比较和布尔逻辑的复杂表达式,可以按照“四步括号法”手工还原它的完整计算顺序。我们以下面这行表达式为例:

result = not 10 + 2 * 3 ** 2 > 30 or 8 & 1 == 1 and 5

第1步:扫描并圈出算术运算(等级4~7)

在表达式左侧的 10 + 2 * 3 ** 2 中,包含 **(等级4)、*(等级6)、+(等级7):

  1. 先绑定最高优先级的幂运算:3 ** 2 → 9。
  2. 再绑定乘法:2 * (3 ** 2) → 2 * 9 = 18。
  3. 最后绑定加法:10 + (2 * (3 ** 2)) → 10 + 18 = 28。

第2步:扫描并圈出位运算(等级8~11)

在表达式右侧的 8 & 1 == 1 中,许多初学者以为先算 1 == 1,但查表可知按位与 &(等级9)高于相等比较 ==(等级12)。因此必须先绑定 8 & 1:

  1. 8 的二进制是 1000,1 的二进制是 0001,按位与 (8 & 1) 结果为 0。

第3步:扫描并圈出比较运算(等级12)

算术和位运算归并完毕后,处理比较运算符 > 和 ==:

  1. 左侧比较:(10 + 2 * 3 ** 2) > 30 即 28 > 30,结果为 False。
  2. 右侧比较:(8 & 1) == 1 即 0 == 1,结果为 False。

第4步:按 not > and > or 归并逻辑运算(等级13~15)

此时原表达式已经简化为:not False or False and 5。按照逻辑运算符内部的严格顺序 not(等级13) > and(等级14) > or(等级15):

  1. 先绑定 not:(not False) 得到 True。此时表达式等价于 True or (False and 5)。
  2. 绑定 and:右侧子表达式聚合成 (False and 5)。
  3. 最后计算顶层的 or:由于 or 左操作数已经是 True,触发短路求值,右侧的 (False and 5) 甚至不需要真正执行,整个表达式直接返回 True。
Python运算符优先级表:复杂表达式怎么确定计算顺序
图2:复杂表达式从语法树内层绑定到顶层短路求值的完整推导过程

我们可以在Python 3交互式终端或脚本(推荐Python 3.8及以上环境)中验证上述分步结果与完全加括号后的等价性:

# 文件名:verify_order.py(在任意 Python 3 环境下运行)
expr_raw = not 10 + 2 * 3 ** 2 > 30 or 8 & 1 == 1 and 5
expr_grouped = (not ((10 + (2 * (3 ** 2))) > 30)) or (((8 & 1) == 1) and 5)

print("无括号表达式结果:", expr_raw)
print("全括号表达式结果:", expr_grouped)
print("右半部分单独求值:", ((8 & 1) == 1) and 5)
示例输出: 无括号表达式结果: True
全括号表达式结果: True
右半部分单独求值: False

四、最容易算错的五类优先级陷阱

实际编码中,绝大多数优先级Bug都集中在以下五组看似符合直觉、实则绑定顺序相反的表达式上。

1. 幂运算 ** 与一元负号 -:-3 ** 2 等于 -9 而不是 9

幂运算符 **(等级4)的优先级比左侧的一元正负号 +x、-x(等级5)更高。因此写下 -3 ** 2 时,Python会先算 3 ** 2 得到 9,再取负号得到 -9。但是,如果负号出现在指数位置(即 ** 右侧),例如 2 ** -3,它是唯一合法的结合方式 2 ** (-3) = 0.125。

注意:当你需要对一个负数或带符号变量求幂时,如果直接写字面量,务必加上圆括号写成 (-3) ** 2;如果是变量(如 x = -3; x ** 2),因为变量求值直接得到对象 -3,结果才是 9。

2. 位运算符与比较运算符:x == 1 & 3 会先算 1 & 3

在C或Java等部分场景中,开发者常混用按位与 &、按位或 | 和相等比较 ==。在Python以及Pandas/NumPy数组过滤中,位运算符 &(等级9)、^(等级10)、|(等级11)的优先级全部高于比较运算符 ==、>、<(等级12)。

如果写出 a == 1 & b == 2,Python会将其解析为 a == (1 & b) == 2(即链式比较 a == (1 & b) and (1 & b) == 2),而不是预期的两个条件同时成立。在Pandas筛选DataFrame行时,写成 df[df['a'] > 0 & df['b'] > 0] 更会因为先算 0 & df['b'] 而直接抛出 ValueError 或得到错误结果。

3. 链式比较陷阱:1 == 1 in [1] 与 True == True == False

因为 ==、!=、<、>、in、is 全都处于等级12,一旦它们连在一起写,无论你本意是什么,都会触发链式比较展开:

  • 1 == 1 in [1]:不会先算 1 == 1 得到 True 再算 True in [1];而是被拆解为 (1 == 1) and (1 in [1]),两边都为 True,最终结果为 True。
  • True == True == False:被拆解为 (True == True) and (True == False),结果为 False;但如果加了括号写成 (True == True) == False,左半边算出 True,再算 True == False,虽然这个例子碰巧也是 False,但换成 (1 < 2) == True(结果为 True)与不加括号的 1 < 2 == True(展开为 1 < 2 and 2 == True,结果为 False),结果就会完全相反!

4. not 与比较运算符的位置关系:not a == b 合法,但 a == not b 报错

逻辑非 not(等级13)的优先级低于所有比较运算符(等级12)。因此:

  • 写 not a == b 时,会先计算 a == b,再对结果取反,等价于 not (a == b)。
  • 但如果反过来写 a == not b,由于 == 优先级比 not 高,解释器在 == 右侧期待一个比它优先级更高的表达式,结果撞上了低优先级的 not,会直接抛出语法错误 SyntaxError: invalid syntax。此时必须显式写为 a == (not b)。

5. 海象运算符 := 优先级垫底:if x := get_val() > 0 会把布尔值赋给 x

Python 3.8引入的赋值表达式 :=(等级18)优先级低于除逗号外的所有运算符。如果你写:

# 错误写法:本意是把 len(items) 赋给 n,再判断 n > 3
if n := len(items) > 3:
    print(n)

由于 >(等级12)远高于 :=(等级18),Python会先计算 len(items) > 3 得到布尔值 True 或 False,然后把布尔值赋给变量 n。正确写法必须给赋值表达式加上括号:if (n := len(items)) > 3:。

Python运算符优先级表:复杂表达式怎么确定计算顺序
图3:四类高频运算符优先级陷阱的直觉误判与真实绑定对比

五、用Python内置ast模块直接查看真实计算顺序

如果在实际项目中遇到一行极其复杂的遗留表达式,不想靠人工查表冒改错逻辑的风险,最稳妥的方法是直接使用Python标准库自带的 ast(抽象语法树)模块。ast.parse() 会调用Python解释器自身的语法分析器,将表达式的真实嵌套结构原原本本打印出来,无需安装任何第三方依赖。

操作步骤与验证代码

在本地终端或任意Python 3.9及以上环境(Python 3.9为 ast.dump() 新增了 indent 缩进参数,若是Python 3.8及以下去掉 indent=2 即可)运行以下脚本:

# 文件名:inspect_ast.py(Python 3.9+ 标准库直接运行)
import ast

def show_eval_structure(expr_str: str) -> None:
    tree = ast.parse(expr_str, mode="eval")
    print(f"=== 表达式: {expr_str} ===")
    print(ast.dump(tree.body, indent=2))

# 验证 1:负号与幂运算
show_eval_structure("-3 ** 2")

# 验证 2:位运算与相等比较、逻辑与
show_eval_structure("8 & 1 == 1 and 5")
示例输出: === 表达式: -3 ** 2 ===
UnaryOp(
  op=USub(),
  operand=BinOp(
    left=Constant(value=3),
    op=Pow(),
    right=Constant(value=2)))
=== 表达式: 8 & 1 == 1 and 5 ===
BoolOp(
  op=And(),
  values=[
    Compare(
      left=BinOp(
        left=Constant(value=8),
        op=BitAnd(),
        right=Constant(value=1)),
      ops=[Eq()],
      comparators=[Constant(value=1)]),
    Constant(value=5)])

如何阅读 ast.dump() 的输出:

  • 缩进越深(越往里层)的节点,优先级越高,越先绑定成一个整体:在第一个输出中,最外层是 UnaryOp(op=USub())(一元负号),而它的操作数 operand 里面包裹着 BinOp(op=Pow())(幂运算 3 ** 2),一眼就能确认是先算平方再取负。
  • 在第二个输出中,最内层是 BinOp(op=BitAnd())(8 & 1),往外一层是 Compare(ops=[Eq()])(== 1),最外层才是 BoolOp(op=And())(and 5),层级关系一目了然。

六、复杂表达式顺序判定与代码重构总结

掌握运算符优先级表的核心目的,是在阅读他人代码时能准确推导出执行结果,而在自己编写代码时主动消除歧义。当表达式跨越多于两个优先级梯队(例如同时混用算术、位运算与多种布尔逻辑)时,建议遵循以下工程实践:

  1. 基本的“乘除先于加减、比较先于 and/or”可以不加括号:例如 price * count + shipping > 100 and is_vip 符合所有开发者的普遍直觉,保留原样更加简洁。
  2. 凡是涉及位运算(&、|、^、<<、>>)、幂运算配负号、三元表达式嵌套、and 与 or 混合、海象运算符 :=,一律显式加上圆括号 ():圆括号在Python中属于最高优先级(等级1),不会带来任何额外的运行时性能损耗。
  3. 超过3层逻辑组合时,拆分为具名布尔变量:与其在一行里堆砌 8 个运算符,不如将子条件提取为语义清晰的中间变量(如 has_quota = ...、is_valid_user = ...),既能固定求值顺序,也方便在调试器中单步查看每个子条件的真假值。
Python运算符优先级表:复杂表达式怎么确定计算顺序
图4:确定复杂表达式计算顺序与防错重构的四步闭环流程
相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表