8

假设我有一些 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)

这是理智的,而不是太多的性能损失吗?在网络应用程序的情况下。

非常感谢。

4

3 回答 3

5

字典的无序性质意味着如果s 的顺序不同(例如,如果一个属性以不同的顺序分配,则可能会发生这种情况),tuple(self.__dict__.values())则很容易产生不同的结果。dict

因为你values是可散列的,你可以试试这个:

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

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

return hash(self.PersonID)

假设PersonID在实例中是相对唯一的。

于 2013-08-26T03:48:28.130 回答
1

如果您已经在使用__dict__平等 for ,那么不使用for__eq__会有点愚蠢。但是,给出了一个任意排序的列表,其中不包含有关哪个值对应于哪个属性的信息,因此该代码实际上不起作用。相反,您可以尝试__dict____hash__values

return hash(tuple(sorted(self.__dict__.viewitems())))

或者

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

两者都将消除排序问题并保留属性名称信息。

于 2013-08-26T03:52:01.503 回答
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__不应该直接调用的答案中发现,所以我==改用了。

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

于 2017-04-20T21:57:47.363 回答