Python中将类作为字典键

3

所以,我已经对此进行了大量的研究,看到了各种其他链接并参考了Python文档,但是仍然不太清楚。

也许我在Python中看待类的方式有点错误。

据我所知,字典中的键必须是不可变的。然而,由于默认的哈希实现(我想是这样?),类可以成为字典中的键。既然类是可变的,为什么会出现这种情况呢?

例如,

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])

为什么这样可以呢?

“为什么在类中更改属性值后哈希值不变?” 因为默认情况下,哈希()函数的输出与属性的内容无关(除非您显式地覆盖其实现)。 我的答案是正确的,但是关于哈希(c) == id(c) 的部分是不正确的,我正在尝试理解其中的原因。 - cs95
这回答了你的问题吗?Python中默认的__hash__是什么? - Guru Stron
不确定,但我认为对象生成的哈希与其类和/或内存元数据有关,而不是其属性。因此,由于这些元数据不会更改,哈希值也不会更改。 - L.Stefan
字典中键的要求是可哈希的(详见 https://docs.python.org/3/library/stdtypes.html?highlight=dict#mapping-types-dict),并非不可变的。事实上,甚至可以通过子类化 list 并定义 __hash__ 方法,在字典中包含可变对象。此外,将值放入字典后还可以更改哈希值。当然,这是不好的做法。 - ken
你并没有将类作为键使用。只是一个实例 - Kelly Bundy
1个回答

5

你的 C 类的实例实际上是可哈希的,它带有一个默认实现 __hash__,它与该对象的标识相关:

>>> hash(c) # calls c.__hash__()
306169194

这个__hash__实现允许您的实例在字典中用作键。

这解释了“为什么更改类中的内容不会更改哈希值?”——因为即使其内容发生变化,实例的身份/引用也不会改变。

在旧版本的Python中,这与对象的id完全相等,但从Python 3开始,它似乎是其某种派生物。 此帖子详细介绍了哈希实现的细节。


现在假设您想要防止使用您的类实例作为键,您可以按照文档中的方法进行操作:

如果一个没有覆盖 __eq__() 的类希望禁用哈希支持,则应在类定义中包含 __hash__ = None

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

    __hash__ = None # suppressing hash support

c = C()

现在,如果您尝试检索c的哈希值,您将会得到一个听起来很熟悉的TypeError

>>> hash(c)
# TypeError: unhashable type: 'C'

自然,这也意味着您不能再将c用作字典键(也就是说,尝试初始化{c: 15}也会抛出相同的TypeError,因为c不可哈希)。


为什么在类中更改内容不会改变哈希值? - John
2
@John 在你的问题下回答了,但总结一下,hash() 的默认实现是返回一个与对象的引用/标识有关的静态数字,与其内容无关。 - cs95
@John @KellyBundy 哦,谢谢你指出来,这实际上使错误消息更加相关 TypeError: unhashable type: 'C' - cs95

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接