覆盖 __eq__ 和 __hash__ 以比较两个实例的字典属性

Overriding __eq__ and __hash__ to compare a dict attribute of two instances

我正在努力了解如何根据每个实例拥有的基础 dict 属性正确比较对象。

既然我覆盖了 __eq__,我还需要覆盖 __hash__ 吗?我还没有完全掌握 when/where 这样做,真的需要一些帮助。

我在下面创建了一个简单示例来说明我 运行 遇到的最大递归异常。 RegionalCustomerCollection 按地理区域组织帐户 ID。如果区域及其各自的 accountid 相等,则 RegionalCustomerCollection 对象被认为是相等的。本质上,所有 items() 的内容应该相同。

from collections import defaultdict

class RegionalCustomerCollection(object):

    def __init__(self):
        self.region_accountids = defaultdict(set) 

    def get_region_accountid(self, region_name=None):
        return self.region_accountids.get(region_name, None)

    def set_region_accountid(self, region_name, accountid):
        self.region_accountids[region_name].add(accountid)

    def __eq__(self, other):
        if (other == self):
            return True

        if isinstance(other, RegionalCustomerCollection):
            return self.region_accountids == other.region_accountids

        return False 

    def __repr__(self):
        return ', '.join(["{0}: {1}".format(region, acctids) 
                          for region, acctids 
                          in self.region_accountids.items()])

让我们创建两个对象实例并用一些示例数据填充它们:

>>> a = RegionalCustomerCollection()
>>> b = RegionalCustomerCollection()
>>> a.set_region_accountid('northeast',1)
>>> a.set_region_accountid('northeast',2)
>>> a.set_region_accountid('northeast',3)
>>> a.set_region_accountid('southwest',4)
>>> a.set_region_accountid('southwest',5)
>>> b.set_region_accountid('northeast',1)
>>> b.set_region_accountid('northeast',2)
>>> b.set_region_accountid('northeast',3)
>>> b.set_region_accountid('southwest',4)
>>> b.set_region_accountid('southwest',5)

现在让我们尝试比较两个实例并生成递归异常:

>>> a == b
... 
RuntimeError: maximum recursion depth exceeded while calling a Python object

您不需要覆盖 __hash__ 来比较两个对象(如果您想要自定义散列,即在插入集合或字典时提高性能,则需要这样做)。

另外,这里有无限递归:

    def __eq__(self, other):
        if (other == self):
            return True

        if isinstance(other, RegionalCustomerCollection):
            return self.region_accountids == other.region_accountids

        return False 

如果两个对象都是 RegionalCustomerCollection 类型,那么您将有无限递归,因为 == 调用 __eq__.

您的对象不应该 return 散列,因为它是可变的。如果你把这个对象放入字典或集合中,然后再更改它,你可能再也找不到它了。

为了使对象不可散列,您需要执行以下操作:

class MyClass(object):
    __hash__ = None

这将确保对象不可散列。

 [in] >>> m = MyClass()
 [in] >>> hash(m)
[out] >>> TypeError: unhashable type 'MyClass'

这是否回答了您的问题?我怀疑不是因为您明确在寻找 hash 函数。

就您收到的 RuntimeError 而言,这是因为以下行:

    if self == other:
        return True

这会让你进入无限递归循环。请尝试以下操作:

    if self is other:
        return True