我在两个类的对象之间有一个递归关系:Foo
有一组Bar
对象(在它的bars
属性),每个 Bar
都有一个 Foo
对象的 list
(在其 foos
属性中)。我已经实现了如下面的MWE所示,但测试失败。为什么?
根据Python Glossary ,一个set
只能包含hashable
对象和hashable
对象:
An object is
hashable
if it has a hash value which never changes during its lifetime (it needs a__hash__()
method), and can be compared to other objects (it needs an__eq__()
method).
我真的不知道下面的 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。
最佳答案
问题在于,您的 Bar
的哈希值在您将其添加到 Foo.bars
后立即发生了变化。如果您在 add_foo
方法中添加一些 print
语句,您可以看到这一点:
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
正如您所期望的。没有理由它不会t。我怀疑调试时出现了某种错误。)
使单元测试工作的一个简单方法是交换 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
,这将无济于事。如果您在将 Bar
对象添加到集合或字典后调用 add_foo
,您将再次遇到同样的问题。
我认为很明显,具有不一致哈希值的相互依赖的类是一个糟糕的设计选择。可能的解决方案包括
- 删除
Foo
->Bar
->Foo
依赖循环 - 寻找一致的哈希方法
关于python - 为什么在该可哈希对象的集合中找不到我的可哈希对象,该集合是另一个对象的属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49451001/