关于可哈希对象解释的说明需要解释
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
这个类的实例是可以被哈希的,前提是你承诺永远不去重置它们的id
或name
。根据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))