Python基础指南之运算符优先级对照表与记忆方法详解
一、开篇
先看一段代码,你能确定它的输出吗?
# 猜猜输出什么?
result = 2 + 3 * 4
print(f"2 + 3 * 4 = {result}") # 14 还是 20?
result = 10 - 5 - 2
print(f"10 - 5 - 2 = {result}") # 3 还是 7?
result = 2 ** 3 ** 2
print(f"2 ** 3 ** 2 = {result}") # 64 还是 512?
result = not True and False or True
print(f"not True and False or True = {result}") # ?
result = 3 > 2 == True
print(f"3 > 2 == True = {result}") # ?
result = 10 / 2 * 5
print(f"10 / 2 * 5 = {result}") # 25.0 还是 1.0?
怎么样,你答对了几题?如果你对其中任何一道题感到不确定,说明你对Python运算符优先级的理解还有模糊地带。
Python中共有50多种运算符,它们到底谁先算、谁后算?什么时候必须加括号、什么时候可以省略?本文将从优先级表、记忆方法、实际案例三个维度,帮你彻底搞懂运算符优先级这个重要但容易被忽视的知识点。
二、Python运算符优先级完整对照表
2.1 从高到低的优先级总表
| 优先级 | 运算符 | 说明 |
|---|---|---|
| 1 | (expr...)[expr...]{key: value...}f(arg...)x[index]x[index:index]x.attr | 括号/元组显示 列表显示 字典/集合显示 函数调用 索引/下标 切片 属性访问 |
| 2 | ** | 幂运算 |
| 3 | +x, -x, ~x | 一元正/负/按位取反 |
| 4 | *, /, //, % | 乘/除/整除/取模 |
| 5 | +, - | 加/减 |
| 6 | <<, >> | 左移/右移 |
| 7 | & | 按位与 |
| 8 | ^ | 按位异或 |
| 9 | | | 按位或 |
| 10 | ==, !=, >, <, >=, <=is, is not, in, not in | 比较运算符 身份/成员运算符 |
| 11 | not | 逻辑非 |
| 12 | and | 逻辑与 |
| 13 | or | 逻辑或 |
| 14 | if-else | 条件表达式 |
| 15 | lambda | lambda 表达式 |
| 16 | =, +=, -=, *=, /= 等 | 赋值运算符 |
| 17 | yield | yield 表达式 |
2.2 用代码验证优先级
# === 验证乘法高于加法 === print(2 + 3 * 4) # 14 —— 先算3*4=12,再算2+12=14 print((2 + 3) * 4) # 20 —— 括号改变了优先级 # === 验证一元运算符高于二元运算符 === print(-3 ** 2) # -9 —— 先算3**2=9,再取负= -9 print((-3) ** 2) # 9 —— 先取负得-3,再平方=9 # === 验证幂运算从右向左 === print(2 ** 3 ** 2) # 512 —— 先算3**2=9,再算2**9=512 print((2 ** 3) ** 2) # 64 —— 先算2**3=8,再算8**2=64 # === 验证比较运算符优先级高于逻辑运算符 === print(3 > 2 == True) # True —— 等价于 (3 > 2) and (2 == True) # 3 > 2 是 True, 2 == True 也是 True(True被当作1),所以True # === 验证乘除优先级相同,从左向右 === print(10 / 2 * 5) # 25.0 —— (10/2)*5 = 5*5 = 25.0 print(10 / (2 * 5)) # 1.0 # === 验证 not > and > or === print(not True and False or True) # = ((not True) and False) or True # = (False and False) or True # = False or True # = True
三、优先级规则的深层理解
3.1 结合性:同优先级的运算符怎么算
大多数运算符具有左结合性(从左向右计算),但有几个例外:
# === 左结合(从左向右)—— 大多数运算符 === print(10 - 5 - 2) # 3 —— (10-5)-2 = 3 print(100 / 10 / 2) # 5.0 —— (100/10)/2 = 5.0 print(10 + 20 - 5) # 25 —— (10+20)-5 = 25 # === 右结合(从右向左)—— 特殊运算符 === # 1. 幂运算 ** 是右结合 print(2 ** 3 ** 2) # 512 —— 2 ** (3 ** 2) = 2 ** 9 = 512 # 2. 一元运算符是右结合 print(- - 5) # 5 —— -(-5) = 5 # 3. 赋值运算符是右结合 a = b = c = 10 # 等价于 a = (b = (c = 10)) # c=10 → b=10 → a=10 # 4. 条件表达式是右结合(但不建议嵌套使用) x = "正数" if a > 0 else "零或负数" # 嵌套时要注意可读性
3.2 比较运算符的链式特性
比较运算符有一个特殊之处——它们可以链式使用,但这不是优先级导致的,而是Python语法的特殊设计:
# 链式比较的等价展开 a < b < c # 等价于 (a < b) and (b < c) # 注意:不等于 (a < b) < c! # 验证 print(1 < 3 < 5) # True —— (1 < 3) and (3 < 5) print(1 < 3 > 2) # True —— (1 < 3) and (3 > 2) # 混合比较和逻辑运算符 print(1 < 3 and 5 > 2) # True —— 比较优先于and # 等价于 (1 < 3) and (5 > 2)
3.3 短路求值 vs 优先级
短路求值可能导致某些表达式根本没被执行,但这不影响优先级规则:
# 优先级决定了哪些操作数"绑定"到哪个运算符
# 短路求值决定了表达式的哪些部分实际被执行
def show(v):
print(f" 计算了 show({v})")
return v
# 由于and优先级高于or,所以:
# True or False and show(1)
# = True or (False and show(1))
# 短路:True已经确定or的结果,show(1)不会执行
print("True or False and show(1):")
result = True or False and show(1)
print(f" 结果: {result}")
# 但如果改成:
# (True or False) and show(1)
# show(1)会执行,因为and的左边暂时未知
print("(True or False) and show(1):")
result = (True or False) and show(1)
print(f" 结果: {result}")
# 再验证 and 优先级高于 or
print("\n验证优先级: show(0) or show(1) and show(2)")
# 等价于 show(0) or (show(1) and show(2))
# show(0)为假,继续计算show(1) and show(2)
# show(1)为真,需要计算show(2)来确定and的结果
result = show(0) or show(1) and show(2)
print(f" 结果: {result}")
四、高效记忆方法
4.1 分层记忆法
不用死记硬背所有运算符,把它们分成7个层次:
运算符优先级七层记忆法
第一层:访问优先(最高)
() [] {} 函数调用、下标、属性
口诀:先拿数据,再做运算
第二层:一元优先
** 正负号 ~
口诀:幂和符号紧挨数
第三层:算术优先
* / // % (乘除模)
+ - (加减)
口诀:先乘除,后加减(小学就学过)
第四层:移位和位运算
<< >> (移位)
& (按位与)
^ (按位异或)
| (按位或)
口诀:移位先于位,位与、异或、位或依次排
第五层:比较和成员
== != > < >= <= (比较)
is is not in not in(身份和成员)
口诀:比较身份与成员,同级一起算
第六层:逻辑运算
not (逻辑非)
and (逻辑与)
or (逻辑或)
口诀:先非(n)后与(a)再或(o) —— nao!
第七层:赋值最后
= += -= *= ...
口诀:算完再存
"""
4.2 口诀记忆
括号函数最优先,
幂和正负跟后边,
乘除整除与取模,
加减移位居中间,
按位与异或位或,
比较成员一条线,
非与或来再赋值,
lambda 最后站一边。
4.3 可视化记忆
优先级金字塔(从下到上,越来越高)
_________
/ ()[]{} \
/ ** + - ~ \
/ * / // % \
/ + - \
/ << >> \
/ & \
/ ^ \
/ | \
/ == != > < >= <= \
/ is in not \
/ not \
/ and \
/ or \
/ if-else lambda \
/ = += -= *= /= ... \
/_____________________________________\
五、必须加括号的场景
5.1 这不是风格问题,是正确性问题
# 场景1:位运算参与逻辑判断
flags = 0b1010
MASK = 0b1000
# ❌ 错误 —— 位运算优先级低于比较!
if flags & MASK == MASK: # 等价于 flags & (MASK == MASK) = flags & True!
print("匹配") # 结果可能不对
# ✅ 正确 —— 括号明确意图
if (flags & MASK) == MASK:
print("匹配")
# 场景2:左移/右移参与算术运算
# ❌ 模糊
result = 1 << 2 + 3
# 等价于 1 << (2 + 3) = 1 << 5 = 32
# ✅ 明确
result = (1 << 2) + 3 # 4 + 3 = 7
# 场景3:not 作用于复杂表达式
# ❌ 可能误解
if not a > 0 and b > 0:
pass
# 等价于 (not (a > 0)) and (b > 0)
# ✅ 明确意图
if not (a > 0 and b > 0): # 两者不都大于0
pass
if (not a > 0) and b > 0: # a不大于0 且 b大于0
pass
# 场景4:复合赋值与表达式混用
# ❌ 不清晰
x = y = f(z) + g(z) * h(z)
# ✅ 拆分开
temp = f(z) + g(z) * h(z)
x = temp
y = temp
5.2 括号的"黄金法则"
- 当有疑问时,加括号。
- 当没有疑问时——也考虑加括号。
- 你的代码读的人比你写的人多,可读性第一。
# 不加括号:需要查优先级表 result = a << b + c * d & e | f # 加括号:一眼看懂 result = (a << (b + c * d)) & e | f # 更好:拆分成多步 shift_amount = b + c * d shifted = a << shift_amount result = (shifted & e) | f
六、常见优先级错误的真实案例
6.1 案例1:位运算与比较运算符
这是一个真实的bug:
# Bug代码:检查权限位的函数
def has_permission_bug(user_permissions, required):
"""有bug:位运算优先级低于比较"""
return user_permissions & required == required
# 等价于: user_permissions & (required == required)
# = user_permissions & True
# = user_permissions & 1
# 这几乎总是错的!
# 修复后
def has_permission_fixed(user_permissions, required):
"""修复:加括号"""
return (user_permissions & required) == required
# 测试
READ = 0b001
WRITE = 0b010
EXECUTE = 0b100
user_perm = READ | WRITE # 0b011
print(f"Bug版本: {has_permission_bug(user_perm, WRITE)}") # 可能错误
print(f"修复版本: {has_permission_fixed(user_perm, WRITE)}") # True
print(f"修复版本: {has_permission_fixed(user_perm, EXECUTE)}") # False
6.2 案例2:not 的范围
# 这是另一个常见错误
is_active = True
is_admin = False
# ❌ 本意:"如果不是管理员且是活跃用户"
if not is_admin and is_active:
print("可以操作")
# 但这等价于 (not is_admin) and is_active
# 效果:is_admin=False, is_active=True → True(碰巧对了)
# 但如果想表达"不是(管理员且活跃)"?
if not (is_admin and is_active):
print("不是同时为管理员和活跃用户")
# 所以使用not时一定要明确范围
6.3 案例3:海象运算符 :=
# 海象运算符的优先级很低,仅次于逗号
# 几乎总是需要括号!
# ❌ 错误
# if n := len(data) > 0: # 等价于 if n := (len(data) > 0)
# pass # n是True/False,不是长度!
# ✅ 正确
if (n := len(data)) > 0:
print(f"数据长度为{n}")
# 另一个例子
# ❌
# result = x := 10 + 5 # SyntaxError
# ✅
result = (x := 10) + 5 # result=15, x=10
七、实战:构建优先级测试器
class PrecedenceTester:
"""运算符优先级互动测试器"""
def __init__(self):
self.score = 0
self.total = 0
def test_question(self, expression, expected, explanation=""):
"""测试一道优先级题目"""
self.total += 1
try:
actual = eval(expression)
if actual == expected:
self.score += 1
print(f"✅ 第{self.total}题 正确!{expression} = {actual}")
else:
print(f"❌ 第{self.total}题 错误!")
print(f" 表达式: {expression}")
print(f" 你的直觉: {expected}")
print(f" 实际结果: {actual}")
if explanation:
print(f" 💡 解释: {explanation}")
except Exception as e:
print(f"⚠️ 第{self.total}题 异常: {e}")
def run_all_tests(self):
"""运行所有测试题"""
print("=" * 50)
print("Python 运算符优先级测试")
print("=" * 50)
# 算术优先级
self.test_question("2 + 3 * 4", 14, "乘法优先级高于加法")
self.test_question("10 - 5 - 2", 3, "减法从左向右结合")
self.test_question("2 ** 3 ** 2", 512, "幂运算从右向左结合")
self.test_question("-3 ** 2", -9, "幂运算优先级高于一元负号")
self.test_question("(-3) ** 2", 9, "括号改变优先级")
self.test_question("10 / 2 * 5", 25.0, "乘除同级,从左向右")
# 位移和位运算
self.test_question("1 << 2 + 3", 32, "加法优先级高于左移")
self.test_question("(1 << 2) + 3", 7, "括号改变优先级")
self.test_question("5 & 3 == 3", 1, "== 优先级高于 &,小心!")
self.test_question("(5 & 3) == 3", False, "括号改变优先级")
# 逻辑优先级
self.test_question("not True and False", False,
"not > and: (not True) and False")
self.test_question("not (True and False)", True, "括号改变优先级")
self.test_question("True or False and False", True,
"and > or: True or (False and False)")
self.test_question("(True or False) and False", False, "括号改变优先级")
# 比较和逻辑
self.test_question("3 > 2 and 4 < 5", True, "比较 > and")
self.test_question("3 > 2 == True", True, "链式比较")
# 综合
self.test_question("2 + 3 * 4 ** 2 - 8 / 2",
46.0, "综合优先级测试")
print(f"\n{'='*50}")
print(f"总计: {self.total}题, 正确: {self.score}题")
print(f"得分: {self.score/self.total*100:.0f}%")
# 运行测试
tester = PrecedenceTester()
tester.run_all_tests()
八、开发工具中的优先级检查
8.1 利用IDE的警告
# 现代IDE(PyCharm、VS Code + Pylance)会给出优先级警告 # 例如: flags = 0b1010 MASK = 0b1000 # 当你写: result = flags & MASK == MASK # IDE通常会显示警告:"Equality check is not the same as bitwise and" # 建议改为: result = (flags & MASK) == MASK
8.2 使用AST查看Python如何解析
import ast
def show_precedence(expr_str):
"""使用AST查看Python如何解析表达式"""
print(f"\n表达式: {expr_str}")
try:
tree = ast.parse(expr_str, mode='eval')
print(f"AST结构: {ast.dump(tree, indent=2)}")
except SyntaxError as e:
print(f"语法错误: {e}")
# 演示不同表达式如何被解析
show_precedence("2 + 3 * 4")
show_precedence("not True and False")
show_precedence("1 << 2 + 3")
show_precedence("flags & MASK == MASK")
九、本章小结
本文我们系统学习了Python运算符优先级的所有重要知识:
- 完整的优先级表:从括号(最高)到赋值(最低),一共17个层级。记住:括号 > 幂 > 一元 > 乘除 > 加减 > 移位 > 位运算 > 比较 > not > and > or > 赋值。
- 结合性:大多数运算符左结合(从左向右),但幂运算
**、一元运算符和赋值运算符是右结合(从右向左)。 - 记忆方法:七层分层记忆法 + 口诀记忆 + 优先级金字塔,让记忆不再困难。
- 必须加括号的场景:位运算+比较、not+复杂表达式、海象运算符——这些场景加括号是正确性的要求,不是风格问题。
- 常见错误:
flags & MASK == MASK(位运算被比较运算符截胡)、not a and b(not的范围误解)——这些都是真实代码中的bug来源。 - 工具辅助:IDE的警告、AST模块——利用工具检查优先级问题。
运算符优先级不需要死记硬背。当你对某个表达式的求值顺序不确定时,加括号总是最安全的选择。代码是写给人看的,清晰比简洁更重要。⌨️ 下一篇文章,我们将进入Python流程控制的世界——从if条件判断开始!
以上就是Python基础指南之运算符优先级对照表与记忆方法详解的详细内容,更多关于Python运算符优先级的资料请关注脚本之家其它相关文章!


最新评论