Python中对象ID、哈希值与类实例标识的区别及用途答疑
Python 三类对象标识的设计逻辑与适用场景
很多初学者刚接触自定义类的时候,都会把打印实例带出的地址、id()返回值、hash()返回值混为一谈,实际上三者从设计目标到适用场景完全没有重叠,是服务于不同语言机制的独立设计。
先看复现用的测试代码:
class Person: def __init__(self, name): self.name = name p1 = Person("Allen") print(p1) print(id(p1)) print(hash(p1))
典型CPython环境下的输出如下:
<__main__.Person object at 0x000001AE10CC7FD0> 1847117774800 115444860925
1. 打印实例时输出的地址串
这个值根本不是独立设计的标识,只是默认对象字符串表示方法的拼接内容:
- 如果你写的自定义类没有重写
__repr__方法,Python会调用基类object的默认__repr__实现,自动拼接出<模块名.类名 object at 十六进制数值>格式的字符串,主要作用是调试时给开发者快速肉眼区分不同实例。 - 这个字符串里的十六进制数值,在官方CPython解释器里刚好是对象在C层面的内存首地址,你可以自行换算:例子里的id返回值1847117774800转成十六进制就是
0x1AE10CC7FD0,和打印串里的地址完全一致。但这只是CPython的实现细节,不是通用规范——你完全可以重写__repr__返回任意内容,哪怕不带任何地址信息也不会影响代码运行,Python解释器本身不会拿这个打印串里的数值做任何逻辑判断。 - 适用场景:仅用于调试打印、日志输出时给人看,没有任何程序逻辑层面的约束力。
2. id() 函数返回的对象ID
这是Python语言规范明确定义的对象唯一身份标识,有强制的规则约束:
- 规则非常明确:一个对象从创建到被回收的整个生命周期内,
id()返回值固定不变;同一时刻内存中存活的两个不同对象,id()返回值绝对不重复。 - 适用场景:所有需要判断两个变量是否指向同一个内存对象的场景,我们常用的
a is b身份判断,本质就是Python在底层判断id(a) == id(b)。 - 注意:CPython里用内存地址当id值只是实现选择,不是所有Python解释器都这么做(比如PyPy的id实现就和内存地址无关),写代码时不要依赖"id等于内存地址"这个特性。
3. hash() 函数返回的哈希值
这是专门为哈希表结构设计的数值,服务于字典、集合这类基于哈希实现的容器:
- 它的核心规则只有一条:如果两个对象用
==判断相等,那它们的hash()返回值必须相等,否则字典、集合就无法正常找到存入的键。除此之外没有其他强制要求:不同对象可以有相同的哈希值(也就是哈希碰撞,哈希表自带碰撞处理逻辑);同一个对象在不同进程、不同Python版本下的哈希值可以不一样(比如Python默认开启字符串哈希随机化,每次启动进程字符串的哈希值都会变化,用来防止哈希碰撞攻击)。 - 例子里的自定义类没有重写
__eq__和__hash__方法,默认继承的object的哈希实现是基于对象id做数值变换得到的,所以哈希值和id值不相等,这是正常现象。如果是重写了__eq__的不可变对象,哈希值会基于对象的内容计算,和id完全无关——比如两个内容一样的字符串,哪怕是不同的对象(id不同),哈希值也会相等。 - 适用场景:仅用于哈希表的存取位置计算、哈希类算法实现,绝对不能拿哈希值当对象的唯一标识用。
设计逻辑总结
三者完全是为不同需求设计的,不存在功能重叠:
- 打印带出的地址是给开发者看的调试文本,无逻辑约束力
id()返回值是给解释器判断对象身份用的,保证对象生命周期内全局唯一hash()返回值是给哈希容器做索引计算用的,只要求相等对象哈希一致,不要求全局唯一
内容的提问来源于stack exchange,提问作者Ali
相关产品推荐
相关产品推荐

