You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中可变类实例可作为字典键的原因解析

Python类实例作为字典键的原理:可变对象为何能正常工作?

核心误解:字典键的要求不是“不可变”

字典对键的核心要求是可哈希(hashable),而非“不可变”。可哈希的定义是:

  • 对象拥有一个在生命周期内永不改变的哈希值(通过__hash__()方法获取)
  • 可以通过__eq__()方法与其他对象判断相等性

“不可变”只是字符串、元组这类常见可哈希对象的特征,但不是成为字典键的必要条件——只要对象满足上述两个要求,哪怕内部状态可变,也能作为字典键。

默认类实例的哈希与相等性逻辑

Python自定义类的实例,默认行为遵循以下规则:

  • __hash__()方法返回对象的id()值(唯一标识内存地址的整数)
  • __eq__()方法仅比较对象身份(即判断a is b是否为True)

这意味着:

  • 只要实例未被销毁,它的哈希值永远不会改变,无论你修改多少属性
  • 只有当两个实例是同一个内存地址的对象时,才会被判定为相等

你的代码为何能正常运行?

看你给出的示例代码:

class C:
    def __init__(self):
        self.val = 15
        self.array = []

c = C()

D = {c: 15}
c.val = 14
c.array.append(15)

print(D[c])  # 输出15

你修改的是实例c的属性,但c本身的内存地址(身份)没有变化,哈希值也保持不变。当执行D[c]时,字典会计算c的哈希值找到对应存储桶,再通过__eq__比较桶内键与c的身份——因为是同一个对象,匹配成功,所以能返回对应值。

注意:自定义__eq__会破坏默认行为

如果给类自定义了__eq__方法(比如基于属性值判断相等),但未同步自定义__hash__方法,Python会自动将类的__hash__设为None,此时实例会变成不可哈希类型,无法作为字典键,执行时会抛出TypeError: unhashable type异常。

示例:

class C:
    def __init__(self):
        self.val = 15
    def __eq__(self, other):
        return self.val == other.val

c = C()
D = {c: 15}  # 报错:TypeError: unhashable type: 'C'

这是因为自定义__eq__后,默认基于身份的哈希逻辑不再合理——若两个不同实例的属性值相等,它们应该有相同的哈希值,但默认__hash__无法满足,因此Python会禁用哈希,强制你实现与__eq__逻辑匹配的__hash__方法。

总结

  • 字典键的核心要求是可哈希,而非“不可变”
  • 默认类实例的哈希与相等性基于对象身份,因此即使属性可变,只要对象本身存在,就能正常作为字典键
  • 自定义__eq__时必须同步自定义__hash__,否则实例会变为不可哈希类型

内容的提问来源于stack exchange,提问作者flameshooter09

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 16:17:21