is判断对象身份(内存地址),==判断值相等(调用__eq__);仅对none、true、false等单例用is安全,数值和字符串比较必须用==。

is 判断的是对象身份,== 判断的是值相等
Python 里 is 和 == 看似都能比“是不是一样”,但底层干的事完全不同:is 检查两个变量是否指向**同一个对象**(即内存地址是否相同),而 == 调用的是对象的 __eq__ 方法,比的是“逻辑上是否相等”。这意味着哪怕两个对象内容一模一样,只要不是同一个对象,is 就返回 False。
常见错误现象:if x is True: 或 if x is None: 写得顺手,但换成 if x is 1: 就可能翻车——因为整数小常量(-5 到 256)会被缓存复用,超出范围就不再稳定。
- 只对
None、True、False这类单例用is是安全且推荐的 - 拿
is比字符串、列表、自定义对象,基本等于赌运气 -
==可被重载,比如numpy.array([1,2]) == numpy.array([1,2])返回的是布尔数组,不是单个True
小整数和短字符串的 is 行为是 CPython 实现细节,别依赖
CPython 为了节省内存,会把常用的小整数(-5 到 256)和某些短字符串(如标识符风格的)放进对象池,重复创建时直接复用。所以 100 is 100 是 True,但 1000 is 1000 在交互式环境里有时是 True、有时是 False——取决于是否在同一个编译单元里被优化。
使用场景:你写脚本时偶然发现 "hello" is "hello" 成立,但这只是因为解释器做了字符串驻留(interning),不是语言规范保证的行为。换到 PyPy 或某些嵌入式 Python 环境,结果可能不同。
立即学习“Python免费学习笔记(深入)”;
- 不要用
is来判断字符串内容是否相同,一律用== - 不要靠
is测试数字是否等于某个值,除非是None或布尔单例 - 想确认对象身份?用
id(a) == id(b)更直白,也更容易被理解意图
自定义类中 == 的行为由 __eq__ 控制,is 完全绕过它
当你定义一个类,默认的 == 行为其实是继承自 object 的,也就是退化成 is——比较的是对象身份。所以如果你没写 __eq__,a == b 和 a is b 效果一样;但一旦你实现了 __eq__,== 就按你的逻辑走,is 却始终只看地址。
示例:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return isinstance(other, Point) and self.x == other.x and self.y == other.y
<p>p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2) # True
print(p1 is p2) # False- 实现
__eq__时,建议同时实现__hash__(如果对象要进 set 或作 dict 键) - 若
__eq__返回NotImplemented,Python 会尝试调用other.__eq__(self),这是双向比较机制 -
is永远不会触发任何魔术方法,它就是纯粹的指针比较
None 检查必须用 is,不能用 ==
这是唯一被 PEP 8 明确规定的例外:if x is None: 是标准写法,if x == None: 不仅慢(触发 None.__eq__),还危险——因为用户可以重载 __eq__ 让 == None 返回 True,即使对象根本不是 None。
示例:
class BadClass:
def __eq__(self, other):
return True # 故意捣乱
<p>x = BadClass()
print(x == None) # True ← 错误地通过了
print(x is None) # False ← 正确拒绝-
is None是最快的 None 检查方式,也是唯一语义明确的方式 - 同理,
is True和is False也比== True更安全,但更常见的是直接写if x:或if not x: - 别写
if x is not None:的反模式变体,比如if not x is None:,可读性差且容易误读
最易被忽略的一点:很多人以为“== 比较值,is 比较地址”就够了,但真正麻烦的是那些看起来像值、实则带状态的对象——比如浮点数 nan,它连自己都不等于自己(float('nan') == float('nan') 是 False),这时候 is 也救不了你,得用 math.isnan()。底层逻辑清楚,不代表所有边界都自动覆盖。










