1

我在两个类的对象之间有递归关系:Foo有一个对象(在其属性中),每个都有一个对象(在其属性中)。我已经实现如下 MWE 所示,但测试失败。为什么?setBarbarsBarlistFoofoos

根据Python Glossary, aset只能包含hashable对象和hashable对象:

一个对象是hashable如果它有一个在其生命周期内永远不会改变的哈希值(它需要一个__hash__()方法),并且可以与其他对象进行比较(它需要一个__eq__()方法)。

我真的不知道我下面的 MWE 是否满足对象具有永远不会更改的哈希,因为哈希取决于列表中的其他对象并设置属性。有没有办法解决这个问题?

最小的工作示例:

import unittest


class Test(unittest.TestCase):
    def test_foo_bar(self):
        foo = Foo()
        bar = Bar()
        bar.add_foo(foo)

        print(bar)
        print(foo.bars)
        print(hash(bar))
        for bar in foo.bars:
            print(hash(bar))
        # The previous print lines print the following:
        # <mwe2.Bar object at 0x105ba8080>
        # {<mwe2.Bar object at 0x105ba8080>}
        # -9223096319794529578
        # -9223096319794529578

        # The following assertion is OK
        self.assertTrue(bar in {bar})

        # The following assertion fails with AssertionError: False is not true
        self.assertTrue(bar in foo.bars)



class Foo:
    def __init__(self):
        self.bars = set()

    def __hash__(self) -> int:
        return hash(self.__dict__.values())


class Bar:
    def __init__(self):
        self.foos = list()

    def __hash__(self) -> int:
        return hash(tuple(self.foos))

    def add_foo(self, foo: "Foo"):
        foo.bars.add(self)
        self.foos.append(foo)


if __name__ == '__main__':
    unittest.main()

我使用 CPython,Python 3.6.x。

4

1 回答 1

3

问题是你Bar的哈希值在你添加到Foo.bars. print如果在方法中添加一些语句,您可以看到这一点add_foo

def add_foo(self, foo: "Foo"):
    foo.bars.add(self)
    print(hash(self))
    self.foos.append(foo)
    print(hash(self))

输出:

3527539
957074234

这是因为哈希是基于 计算的self.foos,所以任何修改都会self.foos改变对象的哈希。

(旁注:与问题中所述相反,bar in {bar}评估结果True如您所料。没有理由不这样做。我怀疑调试时出现了某种错误。)


使单元测试工作的一个简单方法是将两行代码交换add_foo

def add_foo(self, foo: "Foo"):
    self.foos.append(foo)  # append first
    foo.bars.add(self)  # add to the set with the new hash

输出:

Ran 1 test in 0.000s

OK

add_foo然而,这不是一个真正的解决办法:如果你不止一次调用它也无济于事。如果您add_foo在将Bar对象添加到集合或字典后调用,您将再次遇到同样的问题。

我认为很明显,具有不一致散列的相互依赖的类是一个糟糕的设计选择。可能的解决方案包括

  1. 删除Foo-> Bar->Foo依赖循环
  2. 寻找一致的哈希方法
于 2018-03-23T13:59:13.107 回答