如何为我的类实现__cmp__()和__hash__()方法?

4

我希望编写一个类,可以用作哈希集合(例如在dict中)的键。我知道用户类默认是可哈希的,但在这里使用id(self)是错误的。

我的类将一个tuple作为成员变量。从tuple派生似乎不是一个选项,因为在我的构造函数中,我没有得到与tuple构造函数相同类型的参数。但也许这不是一个限制?

我需要的基本上是一个tuple的哈希值,就像一个真正的tuple会给出它的哈希值。

hash(self.member_tuple)就是这样做的。

这里的想法是两个元组可以相等而它们的id不相等。

如果我按以下方式实现我的__cmp__()

def __cmp__(self, other):
    return cmp(self, other)

这会自动使用hash(self)进行比较吗?还是我应该按照以下方式实现:

def __cmp__(self, other):
    return cmp(self.member_tuple, other)

我的__hash__()函数被实现为返回所持有的tuple的哈希值,即:

def __hash__(self):
    return hash(self.member_tuple)

基本上,__cmp__()__hash__()之间如何交互?我不知道在__cmp__()中,other是否已经是哈希值,我是否应该与“我的”哈希值进行比较(这将是所持有的tuple的哈希值),还是与self进行比较。
那么哪个是正确的呢?
有人能解释一下并可能给我指出文档吗?
1个回答

6
我不建议使用__cmp__方法,而是使用__eq__方法。对于哈希来说,这已经足够了,您不需要让它可排序。此外,在Python 3中,__cmp__已被移除,取而代之的是富比较方法(__eq____lt____gt__等)。
接下来,您的__eq__应该在成员元组相等时返回True:
def __eq__(self, other):
    if not isinstance(other, ThisClass):
        return NotImplemented
    return self.member_tuple == other.member_tuple

当另一个对象的类型不同时,返回NotImplemented单例是一个好的实践,因为这将把等式测试委托给其他对象;如果它没有实现__eq__或也返回NotImplemented,Python将回退到标准的id()测试。
您的__hash__实现很好。
因为哈希值不是唯一的(它只是选择哈希表中的一个槽位的方法),所以等号用于确定匹配键是否已经存在或哈希冲突是否发生。因此,如果要散列对象的插槽为空,则不调用__eq__(或__eq__不存在则调用__cmp__)。
这意味着,如果两个对象被视为相等(a.__eq__(b)返回True),那么它们的哈希值也必须相等。否则,您可能会得到一个损坏的字典,因为Python将不再能够确定哈希表中是否已经存在一个键。
如果您的__eq____hash__方法都在执行其职责时委托给self.member_tuple属性,则保持了该属性;您可以信任基本的tuple类型已经正确地实现了这一点。
请参阅hashable词汇表定义object.__hash__()文档。如果您好奇,我已经写了一些关于dictset类型内部工作原理的东西:

谢谢。不过最后一段让我有些困惑。我如何保证哈希值是相同的呢?当对一个tuple进行哈希时,我可以假设它的哈希值是相同的吗?我不可能知道任何给定tuple的哈希值是否与成员元组的哈希值相同,对吗? - 0xC0000022L
1
@0xC0000022L:你正在将哈希函数委托给元组,就像你委托相等性测试一样,因此你很好地保持了该属性。如果元组即使对于相等的元组也开始产生不同的哈希值,那么这将是一个更大的问题,你可以确信这将被非常迅速地解决。 - Martijn Pieters

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