的文档说,一类是可哈希只要它定义__hash__
方法和__eq__
方法。 然而:
class X(list):
# read-only interface of `tuple` and `list` should be the same, so reuse tuple.__hash__
__hash__ = tuple.__hash__
x1 = X()
s = {x1} # TypeError: unhashable type: 'X'
是什么让X
unhashable?
请注意,我必须有相同的列表(在常规平等的条件下)被散列为相同的值; 否则,我会违反有关规定的哈希函数:
唯一需要的属性是它比较相等的对象具有相同的哈希值
该文档也警告说,一个哈希的对象不应该在其一生中进行修改,当然我不修改的情况下, X
创建后。 当然,解释器将不检查反正。
只需将设置__hash__
方法来使的tuple
类是不够的。 你还没有真正告诉它如何以不同的哈希任何。 因为它们是不可变的元组哈希的。 如果你真的想让你的具体例子的工作,它可能是这样的:
class X2(list):
def __hash__(self):
return hash(tuple(self))
在这种情况下,你实际上是如何定义散列您的自定义列表的子类。 你一定要确定它究竟是如何产生的哈希值。 您可以在哈希任何你想要的,而不是使用元组的哈希方法:
def __hash__(self):
return hash("foobar"*len(self))
从Python3文档:
如果一个类没有定义__eq __()方法不应该限定无论是__hash __()操作; 如果它定义__eq __(),但不__hash __(),它的实例将不能用作在可哈希集合的项目。 如果一个类定义了可变对象,并实现了__eq __()方法,它不应该执行__hash __(),因为哈希的集合的实现需要一个关键的哈希值是不可变的(如果对象的散列值发生变化,这将是错误的哈希桶)。
参考文献: 对象.__哈希__(个体经营)
示例代码:
class Hashable:
pass
class Unhashable:
def __eq__(self, other):
return (self == other)
class HashableAgain:
def __eq__(self, other):
return (self == other)
def __hash__(self):
return id(self)
def main():
# OK
print(hash(Hashable()))
# Throws: TypeError("unhashable type: 'X'",)
print(hash(Unhashable()))
# OK
print(hash(HashableAgain()))
你可以和应该做的,根据你的另一个问题,就是:不要继承什么,只是封装的元组。 这是完全正常的在init这样做。
class X(object):
def __init__(self, *args):
self.tpl = args
def __hash__(self):
return hash(self.tpl)
def __eq__(self, other):
return self.tpl == other
def __repr__(self):
return repr(self.tpl)
x1 = X()
s = {x1}
其中收益率:
>>> s
set([()])
>>> x1
()
如果不修改的情况下, X
创建后,你为什么不继承元组?
但我会指出,这其实并不抛出一个错误,至少在Python 2.6。
>>> class X(list):
... __hash__ = tuple.__hash__
... __eq__ = tuple.__eq__
...
>>> x = X()
>>> s = set((x,))
>>> s
set([[]])
我毫不犹豫地说“作品”,因为这不会做你认为它。
>>> a = X()
>>> b = X((5,))
>>> hash(a)
4299954584
>>> hash(b)
4299954672
>>> id(a)
4299954584
>>> id(b)
4299954672
它只是使用对象ID作为哈希值。 当你真正调用__hash__
你仍然得到一个错误; 同样,对于__eq__
。
>>> a.__hash__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor '__hash__' for 'tuple' objects doesn't apply to 'X' object
>>> X().__eq__(X())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor '__eq__' for 'tuple' objects doesn't apply to 'X' object
据我了解,蟒蛇内部,由于某种原因,被检测X
具有__hash__
和__eq__
方法,但不要求他们。
所有这一切的寓意是:只写一个真正的散列函数。 由于这是一个序列对象,将其转换为一个数组和散列,这是最明显的方法。
def __hash__(self):
return hash(tuple(self))