关于可哈希对象解释的说明需要解释

9 投票
1 回答
753 浏览
提问于 2025-04-16 21:16

Mark Ransom 在一个关于哈希的 SO 问题 中回答道:

[...] 一个对象是可哈希的,前提是它的哈希值在其生命周期内永远不变。所以根据官方定义,任何可变的对象都不能是可哈希的,即使它有一个 __hash__() 函数。我之前说的两个条件都必须满足是不对的,因为可哈希本身就意味着必须是不可变的。

我想确认一下,我理解得对不对——即使我不是母语者——所以如果我理解错了,希望有人能纠正我。

假设有这样一个类:

class Author(object):
    def __init__(self, id, name, age):
        self.id = id
        self.name = name
        self.age = age

    def __eq__(self, other):
        return self.id==other.id\
               and self.name==other.name

    def __hash__(self):
        return hash(('id', self.id,
                     'name', self.name))

我明白 __eq__ 允许我用 == 操作符来比较这个类的对象。从 Mark 的回答中,我了解到,即使我的对象 peter = Author(1, "Peter", 33) 有一个 __hash__,它也不是可哈希的,因为我可能会做一些像 peter.age = 43 的操作,这意味着它是可变的。所以,我的 Author 类的对象不是可哈希的,因此不能用作字典的键,对吗?我理解得对吗,还是说我需要更多的解释呢?:-)

1 个回答

11

这个类的实例是可以被哈希的,前提是你承诺永远不去重置它们的idname。根据Python的原则,虽然你不能保证这些属性永远不会被重置(因为我们都是成年人,知道自己在做什么),但是如果你提供的方法会重置__hash__所依赖的属性,那就显得很不专业。

举个例子,如果你在Author的实例上提供了一个set_name的方法,但没有set_id,那么使用这个类的人可以合理地认为__hash__只会依赖于id

如果你想让Author的使用者明确知道他们不应该重置某个属性,你可以通过在属性名前加一个_来标记它为私有属性。如果你还想提供(只读)访问这个属性的方式,可以把它设置为一个属性

class Author(object):
    def __init__(self, id, name, age):
        self._id = id
        self._name = name
        self.age = age      # ages tend to change, so mutable

    id = property(lambda self: self._id)
    name = property(lambda self: self._name)

    def __eq__(self, other):
        return self.id==other.id\
               and self.name==other.name

    def __hash__(self):
        return hash(('id', self.id,
                     'name', self.name))

撰写回答