比较运算符
比较运算符用于判断两个对象之间的关系,返回布尔值 True 或 False。Python 提供了 ==、!=、<、>、<=、>= 六个基本比较运算符,以及独特的链式比较语法,让条件表达更接近数学书写习惯。
基本比较
== 判断两个对象的值是否相等,!= 判断是否不相等。<、>、<=、>= 则用于大小比较。这些运算符的写法与数学符号一致,但 == 使用双等号以区别于赋值运算符 =。
>>> 2 == 2
True
>>> 2 == 3
False
>>> 2 != 3
True
>>> 3 < 5
True
>>> 5 >= 5
True
比较运算符最常见的错误是误用赋值号:
>>> if x = 5:
File "<stdin>", line 1
if x = 5:
^
SyntaxError: invalid syntax # 赋值不能出现在条件中
Python 3.8+ 中,如果确实需要在表达式内赋值并比较,应使用海象运算符 :=。
链式比较
Python 支持数学风格的链式比较,这是许多其他语言不具备的特性。表达式 a < b < c 在 Python 中等价于 a < b and b < c,但只计算一次 b。
>>> x = 5
>>> 3 < x < 10
True
>>> 3 < x and x < 10
True # 等价写法,但 x 被求值两次
链式比较不仅限于同向运算符,可以混合使用:
>>> x = 5
>>> 3 < x <= 5
True # 3 < 5 and 5 <= 5
>>> 5 <= x < 3
False # 5 <= 5 and 5 < 3 → True and False
>>> 1 < x < 10 < 100
True # 可以链任意长度
链式比较中的每个子表达式都会被求值,短路规则依然适用:
>>> def get_value():
... print("被调用了")
... return 5
...
>>> 3 < get_value() < 10
被调用了
True
>>> 10 < get_value() < 3
被调用了
False # 10 < 5 为 False,不再检查 5 < 3
对象比较与 eq
== 比较的是对象的值相等性,而非身份。对于自定义类,Python 默认通过 __eq__ 方法实现比较,如果没有定义,默认行为是比较对象身份(即效果等同于 is)。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.y
>>> p1 = Point(1, 2)
>>> p2 = Point(1, 2)
>>> p1 == p2
True # 值相等
>>> p1 is p2
False # 不是同一个对象
!= 的行为与 == 相反,但如果类定义了 __eq__ 而没有定义 __ne__,Python 会自动从 __eq__ 推导 != 的结果。
>>> p1 != p2
False # 自动推导为 not (p1 == p2)
对于不可比较的类型,比较运算会返回 NotImplemented 或触发 TypeError:
>>> 3 < "5"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'int' and 'str'
Python 3 不再支持不同类型之间的隐式大小比较,这避免了 Python 2 中 3 < "5" 这类令人困惑的行为。
浮点数比较陷阱
浮点数在计算机中以二进制近似存储,导致许多直观上相等的值实际上存在微小差异。这是比较运算中最隐蔽的陷阱。
>>> 0.1 + 0.2 == 0.3
False
>>> 0.1 + 0.2
0.30000000000000004
直接比较浮点数是否相等通常是不可靠的。正确的做法是检查差值是否小于某个容差:
>>> abs(0.1 + 0.2 - 0.3) < 1e-9
True
Python 3.5+ 提供了 math.isclose() 函数,专门用于安全的浮点数比较:
>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
>>> math.isclose(1.0, 1.0000001, rel_tol=1e-6)
True
>>> math.isclose(1.0, 1.0000001, rel_tol=1e-9)
False
math.isclose() 使用相对容差和绝对容差组合判断,比手动写 abs(a - b) < epsilon 更健壮。
特殊浮点值 nan(Not a Number)有独特的比较行为:nan 与任何值(包括自身)比较都返回 False。
>>> import math
>>> nan = float('nan')
>>> nan == nan
False
>>> nan != nan
True # 判断 nan 的唯一可靠方式
>>> math.isnan(nan)
True # 正确做法
序列和容器的比较
Python 支持序列(列表、元组、字符串等)和容器(字典、集合等)的比较。序列比较是逐元素进行的:
>>> [1, 2, 3] < [1, 2, 4]
True # 前两个元素相同,第三个 3 < 4
>>> [1, 2] < [1, 2, 3]
True # 短序列小于长序列(前缀相同的情况下)
>>> "abc" < "abd"
True # 字符串按字典序比较
字典比较在 Python 3 中仅支持 == 和 !=,不再支持 <、> 等大小比较:
>>> {'a': 1} == {'a': 1}
True
>>> {'a': 1} < {'b': 2}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'dict' and 'dict'
比较运算符的返回值
比较运算符总是返回 True 或 False,但 Python 允许布尔值参与算术运算(True 等价于 1,False 等价于 0)。这种特性偶尔被用于技巧性代码,但不推荐在正式项目中使用:
>>> True + True
2
>>> 5 * (3 > 2)
5
比较运算符是条件判断的基石。链式比较让范围检查变得优雅,而浮点数比较的陷阱提醒我们:计算机中的"相等"与数学中的"相等"并非同一概念。对于精确数值比较,应优先考虑 decimal 模块或 math.isclose()。