id() 返回 CPython 中对象的内存地址,对象存活期间不变,销毁后地址可能被复用;小整数和短字符串因对象复用而非地址复用导致 id 相同;不可用作唯一标识符,应改用 uuid 或内容哈希。

因为对象销毁后,内存地址会被复用,id() 返回的就是 CPython 中的内存地址
id() 本质是 CPython 的内存地址
在 CPython(最常用的 Python 实现)中,id() 直接返回对象在堆内存中的起始地址,类型是整数。它不是“逻辑 ID”,也不是全局唯一序列号——就是裸地址。
这意味着:
- 只要对象还活着,
id()值绝不会变 - 对象被
del或离开作用域、引用计数归零后,内存会被回收 - 后续新对象申请内存时,可能恰好分配到同一块地址,于是
id()看起来“重复”了
小整数和短字符串的 id 重复不是“生命周期重叠”,而是对象复用
比如 id(100) == id(100) 成立,不是因为前一个 100 被销毁后地址复用,而是 CPython 在启动时就缓存了 [-5, 256] 范围内的整数对象,所有代码里写的 100 都指向同一个预分配对象。
立即学习“Python免费学习笔记(深入)”;
同理,短字符串(如 "abc")、小元组(如 (1, 2))也会被驻留(intern),它们的 id() 相同,是因为根本没创建多个对象。
这和“生命周期不重叠导致 id 复用”是两回事:前者是主动共享,后者是被动复用。
别拿 id() 当唯一标识符用
常见误用场景:
- 用
id()作字典 key 存长期缓存 → 对象删掉后新对象撞上旧id(),导致逻辑错乱 - 日志里记录
id()用于追踪对象 → 重启或 GC 后无法关联历史记录 - 跨线程传
id()判断对象身份 → 多线程下对象生命周期更难预测,复用概率更高
真正需要稳定标识时,该用 uuid.uuid4()、给对象加 self.uid = uuid4(),或者基于内容生成哈希(如 hash(tuple(sorted(obj.__dict__.items()))))。
is 和 id() 的关系很直接,但也有坑
a is b 在 CPython 中等价于 id(a) == id(b),但它只在两个对象**同时存在**时有意义。
典型陷阱:
-
x = []; y = x; del x; z = []→ 此时id(z)可能等于原来的id(x),但y已不存在,没法比 -
def f(): return [1]; a = f(); b = f()→a is b一定为False,因为两个列表对象生命周期不重叠,不可能是同一个
所以,is 只能判断“此刻是否同一对象”,不能回溯或预测。
真正容易被忽略的点是:你看到两个不同时间点的 id() 相同,不代表它们“曾经是同一个对象”,只代表那块内存被回收又分配了——底层是纯内存管理行为,和对象语义无关。


















