直接回答:dict 查找分两步——先用 hash(key) 定位桶,再在桶内用 == 逐个比较确认。契约要求:相等的对象哈希值必须相等,否则两个"相等"的 key 会落到不同的桶,导致存进去却取不出来;哈希值在对象生命周期内必须稳定,而哈希又由参与相等判断的字段决定,所以这些字段在作为 key 期间不能变。

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __eq__(self, other):
        return (self.x, self.y) == (other.x, other.y)
    def __hash__(self):
        return hash((self.x, self.y))   # 必须和 __eq__ 基于同样的字段

展开解析:几个容易踩的细节。默认 object.__hash__ 基于 id(),和默认的 ==(比身份)一致,所以普通自定义类天然可哈希;一旦你定义了 __eq__ 而不定义 __hash__,Python 会把 __hash__ 置为 None,对象直接不可哈希——这是解释器在强制契约,防止你写出"相等但哈希不同"的对象。反向不成立:哈希相等的对象不必相等,冲突只会拖慢查找(桶内线性比较),不会出错。@dataclass(eq=True, frozen=True) 会自动生成正确配对的一组方法,frozen 同时禁止改字段,保证哈希稳定。

实践要点:把可变对象当 key 是典型 bug——修改字段后哈希变了,dict 里原来的位置再也找不到它,表现为"key 明明在却 KeyError"。确实需要可变行为时,用对象的不可变标识(如主键 id)作 key,把可变数据放在 value 里。

追问方向:为什么 tuple 可哈希而 list 不行?dict 用开放寻址还是链地址解决冲突?

(约 500 字)