Python __hash__用于等值对象

时间:2013-08-26 03:43:01

标签: python hash set

假设我有一些Person实体,我想知道一个是否在列表中:

person in people?

我不关心'对象的ID'是什么,只是他们的属性是相同的。所以我把它放在我的基类中:

# value comparison only
def __eq__(self, other):
    return (isinstance(other, self.__class__) and self.__dict__ == other.__dict__)

def __ne__(self, other):
    return not self.__eq__(other)

但是为了能够在集合中测试相等性,我还需要定义哈希所以......

# sets use __hash__ for equality comparison
def __hash__(self):
    return (
        self.PersonID,
        self.FirstName,
        self.LastName,
        self.etc_etc...
    ).__hash__()

问题是我不想列出每个属性,每次属性更改时我都不想修改哈希函数。

这样做可以吗?

# sets use __hash__ for equality comparison
def __hash__(self):
    values = tuple(self.__dict__.values())
    return hash(values)

这是否理智,而不是 toooo 大部分性能损失?在网络应用的情况下。

非常感谢。

3 个答案:

答案 0 :(得分:4)

字典的无序性意味着tuple(self.__dict__.values())如果dict碰巧被不同地排序(这可能发生,例如,如果一个人的属性分配在不同的顺序)。

由于您的values可以播放,您可以尝试使用此选项:

return hash(frozenset(self.__dict__.iteritems()))

或者,请注意__hash__不需要考虑所有因素,因为当哈希值比较相等时,__eq__仍将用于验证相等性。因此,你可以侥幸逃脱

return hash(self.PersonID)

假设PersonID在各实例之间相对独特。

答案 1 :(得分:1)

如果您已经__dict__使用__eq__相等,那么对__dict__ __hash__使用values会有点愚蠢。但是,return hash(tuple(sorted(self.__dict__.viewitems()))) 给出了一个任意排序的列表,其中不包含哪个值对应哪个属性的信息,因此代码实际上不起作用。相反,你可以尝试

return hash(frozenset(self.__dict__.viewitems()))

{{1}}

这两个都会删除排序问题并保留属性名称信息。

答案 2 :(得分:0)

感谢您提出的好问题。你正在做我想做的事。在阅读完这些答案之后,我做了类似但有一些差异的事情。

def __str__(self):
    return "{}({})".format(type(self).__name__, ", ".join(["{}={}".format(k, self.__dict__[k]) for k in sorted(self.__dict__)]))
def __eq__(self, other):
    return isinstance(other, type(self)) and self.__dict__ == other.__dict__
def __ne__(self, other):
    return not self == other
def __hash__(self):
    return hash(tuple(self.__dict__[k] for k in sorted(self.__dict__)))

我包含了额外信用的字符串方法,因为我在考虑哈希方法后重新编写了这个方法。

我在另一个答案中发现self.__eq__不应该直接调用,所以我使用==代替。

此哈希使用按键排序的类属性值的元组。这将确保元组中的顺序是一致的。如果您对值进行排序而不是大小写,则交换的两个属性将具有相同的哈希值。