飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

运算符优先级

Python 的运算符优先级决定了表达式中多个运算符的计算顺序。当不写括号时,Python 按照严格的优先级规则自动分组。理解完整优先级表、结合性规则以及常见陷阱,是编写正确且可读代码的基础。

完整优先级表

以下表格从高到低列出 Python 运算符的优先级。同一行内的运算符优先级相同。

优先级运算符说明
最高()、[]、{}括号、索引、切片、字典/集合字面量
↑await xawait 表达式
↑**幂运算
↑+x、-x、~x一元正号、一元负号、按位取反
↑*、@、/、//、%乘、矩阵乘、除、地板除、取模
↑+、-二元加、减
↑<<、>>位移
↑&按位与
↑^按位异或
↑|按位或
↑==、!=、<、>、<=、>=、is、is not、in、not in比较运算
↑not x逻辑非
↑and逻辑与
最低or逻辑或
最低if – else条件表达式
最低lambdalambda 表达式
最低:=海象运算符

注意:海象运算符 := 的优先级实际上低于大多数运算符,因此在混合使用时通常需要括号。

结合性

当多个相同优先级的运算符连续出现时,结合性决定计算顺序。

左结合(从左到右):大多数运算符是左结合的,包括 +、-、*、/、//、%、&、|、^、<<、>>、==、!=、<、> 等。

>>> 10 - 5 - 2
3           # (10 - 5) - 2 = 3,不是 10 - (5 - 2) = 7

>>> 100 / 10 / 2
5.0         # (100 / 10) / 2 = 5.0,不是 100 / (10 / 2) = 20.0

>>> 8 >> 2 >> 1
1           # (8 >> 2) >> 1 = 1,不是 8 >> (2 >> 1) = 4

右结合(从右到左):只有幂运算 **、一元运算符和条件表达式是右结合的。

>>> 2 ** 3 ** 2
512         # 2 ** (3 ** 2) = 2 ** 9 = 512,不是 (2 ** 3) ** 2 = 64

>>> - - 5   # 一元负号右结合
5           # -(-5) = 5

括号使用建议

优先级规则的存在是为了减少括号的使用,但代码的可读性永远优先于省略括号。以下情况建议显式加括号:

  1. 混合不同类别的运算符时:
# 容易误解
result = a + b << 2      # 是 (a + b) << 2 还是 a + (b << 2)?

# 清晰写法
result = (a + b) << 2
  1. 比较运算与位运算混合时:
# 危险:& 优先级低于 ==
if flags & MASK == VALUE:    # 解析为 flags & (MASK == VALUE)
    pass

# 正确写法
if (flags & MASK) == VALUE:
    pass
  1. 逻辑运算与比较混合时:
# 虽然正确但可读性差
if a < b and c < d or e < f:
    pass

# 清晰写法
if (a < b and c < d) or e < f:
    pass
  1. 任何你不确定优先级的情况:

"拿不准就加括号"是 Python 社区的最佳实践。多余的括号不会降低性能,但能显著降低维护成本。

常见陷阱

陷阱 1:幂运算与一元负号

这是 Python 中最著名的优先级陷阱。** 的优先级高于一元负号 -,因此 -3**2 被解析为 -(3**2) 而非 (-3)**2。

>>> -3 ** 2
-9          # -(3**2) = -9
>>> (-3) ** 2
9           # 加括号才能得到 9

这与数学中的惯例一致(指数优先于负号),但与部分编程初学者的直觉相反。

陷阱 2:位运算与比较运算

所有位运算符的优先级都高于比较运算符,这导致位掩码检查必须加括号。

>>> 5 & 3 == 1
False       # 5 & (3 == 1) → 5 & False → 5 & 0 → 0 → False

>>> (5 & 3) == 1
True        # 正确的掩码检查

陷阱 3:逻辑运算与比较运算

not 的优先级低于比较运算,因此 not a == b 被解析为 not (a == b),这通常是期望的行为。但如果想表达 (not a) == b,必须加括号。

>>> not 5 == 5
False       # not (5 == 5) → not True

>>> (not 5) == 5
False       # False == 5 → False

陷阱 4:链式比较的隐含逻辑

链式比较 a < b < c 等价于 a < b and b < c,但 and 的优先级低于比较运算,因此链式比较天然正确分组。然而,不要试图在链式比较中混合使用其他运算符:

>>> 1 < 2 < 3
True        # 1 < 2 and 2 < 3

# 不要这样写
>>> 1 < 2 + 1 < 4
True        # 1 < 3 and 3 < 4,虽然正确但可读性下降

陷阱 5:海象运算符的括号

海象运算符 := 的优先级很低,在大多数表达式中需要括号来明确分组。

# 错误:解析为 x := (5 > 3)
if x := 5 > 3:
    print(x)    # True,不是 5

# 正确
if (x := 5) > 3:
    print(x)    # 5

陷阱 6:字符串拼接与算术运算

+ 既用于数字相加也用于字符串拼接,但它们的优先级相同。混合使用时会按从左到右的顺序计算。

>>> "result: " + 5 + 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str

>>> "result: " + str(5 + 3)
'result: 8'

快速记忆口诀

虽然完整记忆优先级表并不现实,但可以遵循以下原则:

  1. 括号最高:任何不确定的地方都用括号。
  2. 幂运算高于乘除:2**3*4 是 (2**3)*4 = 32。
  3. 乘除高于加减:1+2*3 是 1+(2*3) = 7。
  4. 位运算高于比较:比较前先做位运算,但掩码检查要加括号。
  5. 比较高于逻辑非:not a == b 是 not (a == b)。
  6. 逻辑非高于逻辑与,逻辑与高于逻辑或:not a and b or c 是 ((not a) and b) or c。

验证优先级的方法

当对某个表达式的分组不确定时,可以用以下方法验证:

# 方法 1:加括号对比结果
>>> -3**2
-9
>>> -(3**2)
-9          # 确认 ** 优先级更高

# 方法 2:使用 ast 模块查看解析树
import ast
tree = ast.parse("-3**2", mode='eval')
print(ast.dump(tree, indent=2))
# 输出显示 UnaryOp(op=USub(), operand=BinOp(left=Constant(value=3), op=Pow(), right=Constant(value=2)))
# 确认先算 3**2,再取负

运算符优先级是编程语言语法的基础规则。Python 的优先级设计总体上符合数学惯例,但位运算与比较运算的交叉、幂运算与一元运算符的关系,以及海象运算符的低优先级,都是实际编码中容易出错的地方。培养"加括号明确意图"的习惯,比背诵完整优先级表更有价值。

上一页
海象运算符