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

Python中列表不可哈希,含列表属性的类实例为何可哈希?

问题与解答

问题示例

首先定义一个普通列表:

ingredients = ["hot water", "taste"]

尝试打印该列表的哈希值会触发TypeError:

print(hash(ingredients))

>>> TypeError: unhashable type: 'list'

这意味着列表无法用作字典键等需要可哈希类型的场景。

但如果定义一个仅接收列表作为参数的Tea类:

class Tea:
    
    def __init__(self, ingredients: list|None = None) -> None:
        self.ingredients = ingredients
        if ingredients is None:
            self.ingredients = []

创建该类实例并打印其哈希值不会触发错误:

cup = Tea(["hot water", "taste"])
print(hash(cup))

>>> 269041261

这表明该实例是可哈希的,但打印其ingredients属性的哈希值仍会触发预期错误:

print(hash(cup.ingredients))

>>> TypeError: unhashable type: 'list'

甚至可以将该实例用作字典键:

dct = {
    cup: "test"
}

核心疑问:为什么包含不可哈希列表的类实例本身是可哈希的?衍生问题:是否可以通过这种方式将列表(或其他不可哈希类型)用作字典键?

核心问题解答

Python中对象的可哈希性由**__hash__()方法决定。默认情况下,用户自定义类的实例会继承object类的__hash__()实现——该实现基于对象的内存地址**计算哈希值,完全不涉及实例内部的属性内容。

也就是说,只要你没有重写类的__hash__()或__eq__()方法,不管实例内部存储了什么不可哈希的属性,实例本身的哈希值只和它在内存中的位置有关,因此不会触发哈希错误。

列表不可哈希的原因是它属于可变类型,Python没有为其实现合法的__hash__()方法(可变对象的哈希值若随内容变化,会破坏字典、集合等哈希表结构的一致性)。但这和包含它的类实例无关——类实例的哈希计算逻辑不涉及内部属性,自然不受影响。

衍生问题解答

这种方式确实能让“包含列表的对象”作为字典键,但存在明显的逻辑缺陷:

  • 两个内容完全相同的Tea实例会被视为不同的键,因为它们的内存地址不同:
    cup1 = Tea(["hot water", "taste"])
    cup2 = Tea(["hot water", "taste"])
    dct = {cup1: "test1", cup2: "test2"}
    print(dct)  # 会同时存在两个独立的键值对
    
  • 如果后续修改实例的ingredients属性,实例的哈希值不会改变(因为基于内存地址),这会导致逻辑不一致——比如修改cup1.ingredients后,它在字典中的键仍对应原来的哈希值,和实际内容脱节。

如果想让包含列表的对象成为有意义的字典键,需要手动重写__hash__()和__eq__()方法:

  • 将列表转换为不可变类型(如tuple)来计算哈希值
  • 让__eq__()方法基于属性内容判断对象是否相等

示例实现:

class Tea:
    def __init__(self, ingredients: list|None = None) -> None:
        # 将输入的列表转为元组,确保属性不可变
        self.ingredients = tuple(ingredients) if ingredients else tuple()
    
    def __hash__(self):
        # 基于不可变的元组计算哈希值
        return hash(self.ingredients)
    
    def __eq__(self, other):
        if not isinstance(other, Tea):
            return False
        return self.ingredients == other.ingredients

这样,内容相同的实例会被视为同一个键,同时因为ingredients被设为不可变元组,避免了后续修改导致的哈希不一致问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:15:29