🧮

别让优先级坑了你:一文搞懂 Python 运算符与优先级

摘要:算术、比较、逻辑、成员、身份运算符,Python 中的各种运算符到底怎么用?为什么 1 < x < 10 能用但 not x == y 容易踩坑?本文带你一文搞定运算符机制,配合在线代码运行器,带你理清 Python 优先级顺序。

写代码这些年,我见过无数奇形怪状的 Bug。其中有一大类,既没有语法错误,也没有逻辑逻辑漏洞,却能让程序跑出完全意想不到的结果。

去 Debug 时翻来覆去检查,最后才一拍大腿:少写了一个括号,被运算符的「优先级」给坑了。

在 Python 中,运算符有很多种,而且它们的结合顺序并不完全符合人类的常规直觉。今天我们就把这块地基重新夯实一下,只聊干货和最容易踩坑的细节。

顺便提一句,本站刚才上线了代码一键运行功能。文中的代码块右上角都有个「运行」按钮,点击它就能在浏览器里直接测试,非常方便,大家记得多点点试试。

算术运算符:比数学课本多一点

算术运算符是我们最熟悉的,无非是加减乘除。但在 Python 中,有几个符号需要特别留意:

我们可以看一下它们在 Python 中的具体计算表现。记得点击代码右上角的运行按钮去 playground 亲眼看结果:

a = 10
b = 3

print("真除法 10 / 3 =", a / 3)      # 结果是浮点数 3.3333333333333335
print("整除 10 // 3 =", a // b)     # 结果是整数 3
print("取模 10 % 3 =", a % b)       # 余数是 1
print("幂运算 10 ** 3 =", a ** b)   # 10 的 3 次方是 1000

这里有个关于负数整除的经典大坑。比如 -10 // 3,你觉得结果是多少?由于整除在 Python 里执行的是「向下取整」,结果并不是 -3,而是 -4。这个细节在做索引计算或坐标系转换时极其致命。

比较运算符与链式比较的魔法

比较运算符包括 ==!=><>=<=,用于判断两者关系并返回布尔值(TrueFalse)。

这里必须要夸一下 Python 极其贴心的设计:链式比较。你可以直接像数学课本上那样连着写:

age = 25
# 在 Python 中这完全合法,相当于 age > 18 and age < 30
result = 18 < age < 30
print("18 < age < 30 结果为:", result)

这在很多其他编程语言里是不能直接实现的(比如在 C++ 或 Java 里,18 < age < 30 会先计算 18 < age 得到 true,然后把 true 隐式转换成 1 去跟 30 比较,结果完全偏离预期)。Python 默默在底层帮我们做了解析。

逻辑运算符与「短路求值」机制

Python 逻辑运算符只有三个:andornot

除了要记住它们的优先级关系(not > and > or)之外,最核心的概念就是短路求值

这不仅是为了省电提高效率,在写代码时更能作为一种「保护伞」防崩溃:

# 下面是一段防崩溃的经典写法
def 检查列表(数据):
    # 如果 数据 是 None,直接触发短路返回 False,不会执行 len(数据),从而防止报错崩溃
    if 数据 is not None and len(数据) > 0:
        print("列表非空,第一个元素是:", 数据[0])
    else:
        print("空列表或 None")

检查列表(None)
检查列表([42, 100])

你可以试着把 is not Nonelen(数据) > 0 的位置调换一下,再次传入 None,程序就会抛出空指针异常。理解了短路机制,你就能写出既简洁又安全的代码。

身份与成员:is 和 == 绝不是一回事

这是 Python 面试必考的经典概念:

用个接地气的类比:世界上有两个一模一样的苹果,它们的价格和甜度都相同(值 == 是相等的),但它们并不是同一个苹果(身份 is 是不同的)。

看一下代码演示:

list_a = [1, 2, 3]
list_b = [1, 2, 3]
list_c = list_a

print("list_a == list_b:", list_a == list_b) # True,因为内容一模一样
print("list_a is list_b:", list_a is list_b) # False,它们存在内存的不同角落

print("list_a is list_c:", list_a is list_c) # True,因为 list_c 指向了 list_a 的地址

同样的,成员运算符 innot in 可以帮我们快速判断某个元素是否存在于容器中,写起来比写一个 `for` 循环遍历再匹配优雅百倍。

终极对决:Python 运算符优先级总览

当以上所有运算符混在一起时,Python 会按怎样的顺序进行处理呢?

优先级 运算符 描述
1 (最高) () 括号(永远的第一优先级)
2 ** 幂运算
3 +x, -x, ~x 一元正负号、按位取反
4 *, /, //, % 乘、除、整除、取余
5 +, - 加、减
6 <<, >> 按位左移、右移
7 & 按位与
8 ^ 按位异或
9 | 按位或
10 ==, !=, >, >=, <, <=, is, is not, in, not in 所有的比较、身份、成员运算符(同一级)
11 not 逻辑非
12 and 逻辑与
13 (最低) or 逻辑或

💡 提示

如果你仔细观察,会发现比较运算符的优先级比逻辑运算符要高。这就是为什么我们写 x > 5 and y < 10 时,即使不加括号,Python 也会正确地先去计算 x > 5y < 10,再去算 and 逻辑。

避坑总结

看完了长篇理论,我们来看看下面这行非常容易翻车的小代码:

# 目标是:当 x 不是 1 且不是 2 时,结果为 True
x = 2
result = not x == 1 or x == 2
print("not x == 1 or x == 2 运算结果为:", result)

你觉得上面的输出是 True 还是 False

其实答案是 True。因为按照优先级关系,== 先算,然后是 not,最后才是 or。所以这行语句等价于 (not (x == 1)) or (x == 2)。当 x = 2 时,x == 1Falsenot FalseTrue。于是短路机制触发,直接返回了 True,哪怕我们原本想要的是当 x 是 2 时输出 False

正确的逻辑写法必须加上括号:not (x == 1 or x == 2)

虽然我列了一整张优先级表格,但实战中最实用的建议是:看不准的优先级,统统手动套上括号。加个小括号不仅能救你一命,更能让其他人(包括三个月后的你自己)在阅读代码时,一眼就能看清你的真实意图,别指望大家去背优先级表。

如有疑问欢迎留言交流。

💬 评论区 (0)

0/500

加载评论中……

© 2021-2026 小宝科技站 All Rights Reserved
本网站内容未经允许,不得转载。