python集似乎包含两个相同的对象

python集似乎包含两个相同的对象,python,hash,set,Python,Hash,Set,我有两个集合,其中包含自定义对象。我从一个集合中获取对象,并使用set.update将它们添加到另一个集合中 之后,一个集合似乎包含两个相同的对象:它们的哈希相同,它们彼此==而不是!=相互之间。如果我将集合强制转换为列表并返回集合,则新集合中只有一个对象。我没有其他线程或进程运行,可能会在中间以某种方式改变任何对象的状态。 我可以发布我的hash和eq,但它们调用多个其他子对象hash和eq,而且包含的代码会很多 以下是我正在运行的调试代码及其输出: print('old hash', map

我有两个集合,其中包含自定义对象。我从一个集合中获取对象,并使用set.update将它们添加到另一个集合中

之后,一个集合似乎包含两个相同的对象:它们的哈希相同,它们彼此==而不是!=相互之间。如果我将集合强制转换为列表并返回集合,则新集合中只有一个对象。我没有其他线程或进程运行,可能会在中间以某种方式改变任何对象的状态。

我可以发布我的hasheq,但它们调用多个其他子对象hasheq,而且包含的代码会很多

以下是我正在运行的调试代码及其输出:

print('old hash', map(hash, node.incoming_edges))
print('new hash', map(hash, new_node.incoming_edges))
if len(new_node.incoming_edges) > 1:
    node1, node2 = list(new_node.incoming_edges)
    print('eq', node1 == node2)
    print('ne', node1 != node2)
print('type', type(node.incoming_edges))
print('type', type(new_node.incoming_edges))

new_node.incoming_edges.update(node.incoming_edges)

print('combined')
if len(new_node.incoming_edges) > 1:
    node1, node2 = list(new_node.incoming_edges)
    print('eq', node1 == node2)
    print('ne', node1 != node2)

print('combined hash', map(hash, new_node.incoming_edges))
print('len', len(new_node.incoming_edges))

new_node.incoming_edges = set(list(new_node.incoming_edges))

print('len', len(new_node.incoming_edges))
以及有关的输出:

old hash [5805087492197093178]
new hash [5805087492197093178]
type <type 'set'>
type <type 'set'>
combined
eq True
ne False
combined hash [5805087492197093178, 5805087492197093178]
len 2
len 1
old散列[5805087492197093178]
新哈希[5805087492197093178]
类型
类型
合二为一
情商真实
东北错
组合哈希[5805087492197093178,5805087492197093178]
透镜2
透镜1
我在想,由于我的对象是递归图,通过将它们添加到集合中,哈希可能会发生变化,但是我在操作前后打印哈希,以确认哈希没有变化

这怎么可能发生?我很乐意引入更多的调试输出,我可以很容易地复制

另外,在我试图了解正在发生的事情时,这里有一些来自pdb的信息:

 57                     print('type', type(new_node.incoming_edges))
 58     
 59                     import pdb; pdb.set_trace()
 60     
 61                     new_node.incoming_edges.update(node.incoming_edges)
 62  ->                 new_node.outgoing_edges.update(node.outgoing_edges)
 63                     # new_node.incoming_edges = set(list(new_node.incoming_edges))
 64     
 65                     print('combined')
 66                     if len(new_node.incoming_edges) > 1:
 67                         node1, node2 = list(new_node.incoming_edges)
(Pdb) !len(new_node.incoming_edges)
2
(Pdb) !x, y = new_node.incoming_edges
(Pdb) x
<Edge user.id getters={<SQLQuery tables:users; selects:users.last_name; where:{} input_mapping:{'id': 'users.id'}, <SQLQuery tables:users; selects:users.first_name; where:{} input_mapping:{'id': 'users.id'}} setter=None out=False>
(Pdb) y
<Edge user.id getters={<SQLQuery tables:users; selects:users.last_name; where:{} input_mapping:{'id': 'users.id'}, <SQLQuery tables:users; selects:users.first_name; where:{} input_mapping:{'id': 'users.id'}} setter=None out=False>
(Pdb) hash(x)
-8545778292158950550
(Pdb) hash(y)
-8545778292158950550
(Pdb) x == y
True
(Pdb) x != y
False
(Pdb) len(set(list(new_node.incoming_edges)))
1
(Pdb) len(new_node.incoming_edges)
2
57打印('type',type(新节点、传入边缘))
58
59进口pdb;pdb.set_trace()
60
61新的\u节点。传入的\u边。更新(节点。传入的\u边)
62->新建\u节点。传出\u边。更新(节点。传出\u边)
63#新建节点。传入边=设置(列表(新建节点。传入边))
64
65打印(“合并”)
66如果len(新节点传入边)>1:
67节点1,节点2=列表(新节点。传入的边)
(Pdb)!len(新的_节点。传入的_边)
2.
(Pdb)!x、 y=新节点。传入边
(Pdb)x

心理调试:你在
节点
中有
集合
成员,这些成员是在该代码开始之前添加的,然后以一种改变散列的方式进行变异
set
在插入时缓存每个对象的哈希值,在正常情况下不会重新刷新;事实上,从
set
复制或更新到
set
也可以避免重新灰化,因为它可以直接复制缓存的哈希值,而不是重新灰化

当您转换到
列表
(有效地去除缓存的哈希值),然后返回到
(现在必须重新刷新所有元素,从而修复重复)时,“修复”了问题


当然,这不是一个真正的解决办法。真正的解决办法是永远不要使可变项可散列;遵循Python的指导,或者只使用不可变类型,或者允许从可变变量转换为不可变变量,其中只有不可变变量定义
\uuuuuuuuuuuuuuuuuuuuuuuuuuu
。如果你定义了
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
,你就要注册可散列类型的规则,其中包括逻辑不变性(以及散列计算中涉及的所有属性的不变性)。

你说发布你的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhash
\uuuuuuuueq
会有很多,因为它们最终会检查很多子对象。涉及哪些类型的子对象检查?特别是,您是否对易变对象(如边缘端点)进行了任何比较?您的示例中出现了一些问题。这是python 3吗?不可能,您的
print
s将打印
map
对象,而不是列表。这是python 2吗?不可能,您的
打印将打印元组。太棒了,谢谢。听起来不错。我知道集合应该总是只包含不可变的对象,但出于某种原因,我没有将这些点联系起来,这可能是这个bug的原因。我将遍历并只在不可变对象上放置
\uuuuuuuuuuuuuuuuuuuuuuuuu
。谢谢