python set似乎拥有两个相同的对象

时间:2015-10-28 02:36:52

标签: python hash set

我有两套自定义对象。我从一组中获取对象,并使用set.update将它们添加到另一组中。

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

我可以发布哈希 eq ,但他们会调用多个其他子对象哈希 eq 要包括很多代码。

而是这里是我正在运行的调试代码及其输出:

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

我在想,因为我的对象是递归图形,所以哈希可能会通过将它们添加到集合来改变,但是我在操作之前和之后打印哈希以确认哈希没有改变。

这怎么可能发生?我很乐意介绍更多的调试输出,我可以很容易地重现。

P.S。这是来自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

1 个答案:

答案 0 :(得分:2)

心灵调试:您在set中有node个成员在此代码开始之前添加,然后以改变其哈希值的方式进行变异。 set在插入时缓存每个对象的哈希值,在正常情况下不会重新哈希;实际上,从set复制或更新到set也可以避免重复,因为它可以直接复制缓存的哈希值而不是重新哈希。

你&#34;修复&#34;转换为list(有效地剥离缓存的哈希值)时的问题,然后回到set(现在必须重新散列所有元素,这会导致它修复重复)。

当然,这不是一个真正的解决方案。真正的解决方法是从不使可变项目可以清除;遵循Python的主导,并且只使用不可变类型,或允许从可变变量转换为不可变变量,其中只有不可变变量定义__hash__。如果您定义__hash__,则需要注册可输入类型的规则,其中包括逻辑不变性(以及哈希计算中涉及的所有属性的不变性)。