Python中自定义类对象可作字典键,列表却不行?相关疑问解析
为什么Python自定义类对象可作为字典键,列表却不行?
先看你提供的测试代码:
class MyClassA: def __init__(self, value=0): self.value = value class MyClassB: def __init__(self, value=0, list_=[]): self.value = value self.list = list_ a = MyClassA() b = MyClassB() c = [] dict_ = {} dict_[a] = 1 # No error dict_[b] = 1 # No error dict_[c] = 1 # TypeError: unhashable type: 'list'
核心原因:字典键必须是可哈希的
Python字典基于哈希表实现,要求键必须满足两个条件:
- 实现
__hash__()方法,能生成稳定的哈希值 - 实现
__eq__()方法,能判断两个键是否相等
下面针对你的疑问逐一解释:
1. MyClassA/MyClassB的对象是否为不可变对象?
不是。你可以随时修改它们的属性,比如:
a.value = 100 b.list.append("new item")
对象的内部状态是可变的,但它们依然能当字典键——因为Python给自定义类实例默认提供了__hash__()和__eq__()方法:
- 默认
__hash__()基于对象的内存地址生成哈希值,只要对象没被销毁,内存地址不会变,哈希值就稳定。 - 默认
__eq__()只比较对象的内存地址,只有同一个实例才会被判定为相等。
所以哪怕你修改了对象的属性,它的哈希值和相等性判断逻辑都不会变,字典依然能正常找到对应的键。
2. 列表本身也是对象,为何不可哈希?
因为列表是可变容器,它的内容可以随时被修改(比如append()、pop()、直接修改元素值)。如果允许列表作为字典键,一旦你修改了列表内容,它的哈希值就会改变——而字典是通过哈希值来定位键的位置的,哈希值变化后,原来的键就会“丢失”,破坏字典的一致性。
Python为了避免这种问题,直接让可变类型(列表、字典、集合等)不实现有效的__hash__()方法(或者将__hash__设为None),所以当你试图把列表作为字典键时,就会抛出TypeError: unhashable type: 'list'。
额外补充:如何让自定义类更适合做字典键?
如果想让自定义类的实例成为“安全”的字典键,应该把类设计为不可变的。比如用dataclasses的frozen=True:
from dataclasses import dataclass @dataclass(frozen=True) class MyImmutableClass: value: int
这样实例的属性无法被修改,同时__hash__()和__eq__()会基于属性值生成,逻辑相等的实例会有相同的哈希值,更符合字典键的预期。
内容的提问来源于stack exchange,提问作者Na Chen
相关产品推荐
相关产品推荐

