Python 的 is 和 == 有什么区别?为什么判断 None 用 is,比较字符串却不用?
下面是一段教学用的模拟面试。
🧑💻 面试官: Python 的 is 和 == 有什么区别?
🙋♂️ 我: 都能判断两个值相不相等。
🧑💻 面试官: 两张内容相同的列表,is 为什么可能是 False?
🙋♂️ 我: 因为不是同一个对象。
🧑💻 面试官: 那我试了两个相同的小整数,is 又是 True。你会拿这个结果写比较逻辑吗?
“内容一样”和“同一个对象”不是一回事。缓存恰好让对象复用,不能改变我们想表达的判断。
面试速答(60 秒版)
is 判断两个引用是不是指向同一个对象;== 判断它们按类型定义的相等规则是否相等,可能调用自定义的比较逻辑。
两张分别创建的列表,即使元素完全相同,也可以 == 为 True、is 为 False;两个变量指向同一张列表,is 才为 True。
判断 None 通常使用 is None,因为我们要识别这个特定的单例对象,也避免触发对象自定义的相等行为。
比较字符串、数字等值,使用合适的相等比较。不要依赖小整数缓存或字符串驻留时观察到的 is 结果,那不是表达值相等的可靠方式。

图:is 看身份,== 看相等规则。
知识点详解:我们要比较的是对象身份,还是对象内容?
两张一样的清单,也可以是两张纸
分别创建两张列表,内容都为 1、2;再让第三个变量指向第一张列表。
Python
a = [1, 2]
b = [1, 2]
c = a
assert a == b
assert a is not b
assert a is c
a 和 b 的内容相同,但对象不同。c 没有复制列表,只是增加了一个指向 a 的引用。所以改变 a 的内容,c 也会观察到。
TypeScript
const a = [1, 2];
const b = [1, 2];
const c = a;
console.assert(a !== b);
console.assert(a === c);
这段代码只是对照对象身份。JavaScript 的数组严格相等比较按对象身份判断,并不会像 Python 列表的 == 一样自动逐项比较内容。因此没有把两个语言的操作符机械对应。
== 也不是固定的一套“深比较”
Python 的相等关系由类型定义。列表会按其比较规则处理元素,自定义类则可以实现 eq。
因此,不能把 == 概括成“无论什么对象,都递归比较全部字段”。有些类型比较的是业务键,有些比较返回的行为又不同。具体表达式规则见 Python 语言参考。
这也解释了为什么判断 None 更适合 is:我们不是请求某个对象描述它是否“等于空”,而是在判断它是不是 None 本身。
小整数与字符串测试为什么会骗人?
解释器可以复用某些对象,编译方式也可能影响常量如何创建。于是,在某个交互窗口里观察到相同数字的 is 为 True,并不意外。
但业务要判断的是“这两个数的值是否相等”,不是“解释器这次是否复用了同一份对象”。比较字符串也是同样的道理。
不要通过反复换数字找出一个“缓存分界点”,再把这个分界写成语言保证。实现细节不是业务判断条件。

图:None 判断,找的是特定对象。
哨兵值为什么很适合身份比较?
假设函数要区分“没有传参数”和“明确传了 None”。仅用 None 当默认值,就分不清这两种情况。
可以创建一个专属哨兵对象,用 is 判断输入是不是它。这时,身份比较表达的正是我们的意图:只有那一个专门创建的对象代表“未提供”。
也要注意,哨兵的身份依赖正确的传递方式。如果跨序列化、跨进程传递,它不一定还是原来的对象,协议需要另外设计。
面试官继续追问
两个变量 is 为 True,内容一定一样吗?
它们在同一时刻指向同一个对象。不过对象之后可能改变;身份一致不代表内容不可变,也不代表并发访问安全。
id 相同能永久代表同一个对象吗?
不能把已经销毁对象的 id 当永久业务标识。身份值可能复用;用户 ID、订单 ID 等业务身份需要自己的稳定规则。
面试速记卡
- is:判断同一个对象。
- ==:按类型定义判断相等。
- None:用 is None 表达单例身份判断。
- 值比较:不依赖整数缓存或字符串驻留。
- 跨语言:Python 列表 == 与 JS 数组 === 不是同一规则。
公司面试真题
这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。
浏览公司面试真题 →