如果你已经使用Python一段时间了,可能在代码中使用过is
和==
。它们看起来相似,读起来也很像。但在底层,它们的功能却截然不同。当你本意是使用==
时却使用了is
,可能会导致微妙而棘手的错误,这些错误非常难以追踪。
is
和==
之间有什么区别?
==
检查两个值是否相等。is
检查两个变量是否指向内存中的同一个对象。
这里有一个简单的方法来记住它:
👉 == → “这些东西看起来一样吗?”
👉
is
→ “这些东西真的是同一个对象吗?”
快速示例
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True: 内容相同
print(a is b) # False: 内存中的对象不同
尽管 a
和 b
包含相同的列表值,但它们不是同一个对象。Python 创建了两个独立的列表。
危险的地方
问题出现在开发者假设 is
的行为与 ==
相同,或反之,尤其是因为有时 is
由于 Python 的内部优化而“意外”工作。
示例 1:字符串
x = "hello"
y = "hello"
print(x == y) # True
print(x is y) # True? ...有时是
对于 is
来说,这可能是 True
,因为 Python 会“驻留”(重用)短字符串以节省内存。但请看这个:
a = "hello world! this is a very long string"
b = "hello world! this is a very long string"
print(a == b) # True
print(a is b) # False
突然间,is
不再有效,尽管字符串是相等的。为什么?因为 Python 没有对较长的字符串进行驻留。在这里依赖 is
会导致不稳定、不一致的结果,这些结果取决于你无法控制的因素,比如字符串长度或 Python 实现。
示例 2:整数
Python 还缓存了介于 -5
和 256
之间的小整数。
x = 256
y = 256
print(x is y) # True
a = 257
b = 257
print(a is b) # False
编写一些“通过”某些测试但在生产环境中失败的代码是很容易的。这些错误在后期调试时几乎是不可能的。
那么,什么时候应该使用 is
?
使用 is
是有其合理用途的。但这些用途是特定且罕见的。
在检查单例对象时使用 is
例如:
if my_var is None:
...
这是检查 None
的正确方式,因为 None
是一个单例。在 Python 中只有一个 None
对象。
其他有效的情况:
-
my_obj is True
-
my_obj is False
但即使对于 True
和 False
,在数据密集型代码(例如 pandas、NumPy)中,使用 ==
通常更安全,因为这些库重新定义了什么是“真值”。
真实世界的错误场景
以下是我见过的一些真实错误:
错误 1:循环中的错误比较
for item in my_list:
if item is "done": # 哎呀
这可能对某些字符串有效,但对其他字符串则会静默失败。正确的检查是:
for item in my_list:
if item == "done":
break
错误2:数据验证中的假阴性
user_input = input("请输入 yes 或 no: ")
if user_input is "yes": # 即使输入为 'yes' 也可能失败
print("确认!")
你可能永远不会知道这个失败,除非你的用户输入 "yes"
但没有任何反应。
错误 3:不可靠的条件逻辑
x = 1000
y = 10 * 100
if x is y: # 错误!即使值相同
do_something()
这将静默地不执行 do_something()
,即使大多数人会说“1000等于1000。”
何时使用哪一个?
- 除非你100%确定是在检查身份,否则请使用
==
。 - 仅在检查
None
或与哨兵对象比较时使用is
。 - 不要依赖Python的内部优化,如字符串驻留或小整数缓存。这些是实现细节,可能在不同版本或平台之间发生变化。
操作 | 使用 == ? |
使用 is ? |
---|---|---|
值相等 | ✅ | ❌ |
对象身份 | ❌ | ✅ |
与 None 比较 |
❌ | ✅ |
与常量比较(例如,字符串,整数) | ✅ | ❌ |
编写可靠、可移植的代码 | ✅ | ❌(除了 None ) |