python set 似乎包含两个相同的对象
python set seems to hold two identical objects
我有两组,其中包含自定义对象。我从一组中取出对象,然后使用 set.update.
将它们添加到另一组中
之后,似乎一组包含两个相同的对象:它们的散列相同,它们彼此 == 而不是 != 。如果我将集合转换为列表并返回到集合,则新集合中只有一个对象。我没有其他线程或进程 运行 可能会在中间以某种方式改变任何对象的状态。
我可以 post 我的 hash 和 eq 但它们调用多个其他子对象 hash 和 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
心理调试:您在 node
中有 set
成员,这些成员是在此代码开始之前添加的,然后以改变其哈希值的方式发生变异。 set
在插入时缓存每个对象的哈希值,并且在正常情况下不会重新哈希;事实上,从 set
复制或更新到 set
也可以避免重新散列,因为它可以直接复制缓存的散列值而不是重新散列。
当您转换为 list
(有效地剥离缓存的哈希值),然后返回到 set
(现在必须重新哈希所有元素,这使其修复重复)。
当然,这不是真正的解决方法。真正的解决方法是 NEVER 使可变项目可哈希;跟随 Python 的领导,要么只使用不可变类型,要么允许从可变变体转换为不可变变体,其中只有不可变变体定义 __hash__
。如果您定义 __hash__
,则您正在注册可哈希类型的规则,其中包括逻辑不变性(以及哈希计算中涉及的所有属性的不变性)。
我有两组,其中包含自定义对象。我从一组中取出对象,然后使用 set.update.
将它们添加到另一组中之后,似乎一组包含两个相同的对象:它们的散列相同,它们彼此 == 而不是 != 。如果我将集合转换为列表并返回到集合,则新集合中只有一个对象。我没有其他线程或进程 运行 可能会在中间以某种方式改变任何对象的状态。
我可以 post 我的 hash 和 eq 但它们调用多个其他子对象 hash 和 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
心理调试:您在 node
中有 set
成员,这些成员是在此代码开始之前添加的,然后以改变其哈希值的方式发生变异。 set
在插入时缓存每个对象的哈希值,并且在正常情况下不会重新哈希;事实上,从 set
复制或更新到 set
也可以避免重新散列,因为它可以直接复制缓存的散列值而不是重新散列。
当您转换为 list
(有效地剥离缓存的哈希值),然后返回到 set
(现在必须重新哈希所有元素,这使其修复重复)。
当然,这不是真正的解决方法。真正的解决方法是 NEVER 使可变项目可哈希;跟随 Python 的领导,要么只使用不可变类型,要么允许从可变变体转换为不可变变体,其中只有不可变变体定义 __hash__
。如果您定义 __hash__
,则您正在注册可哈希类型的规则,其中包括逻辑不变性(以及哈希计算中涉及的所有属性的不变性)。