2015-10-28 40 views
0

我有兩個自定義對象在其中。我從一組中獲取對象,並使用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 
+0

你說可以發佈您的'__hash__'和'__eq__'將是一個很大的,因爲他們最終確認了大量的子對象。涉及哪些子對象檢查?特別是,你是否在可變對象上進行任何'=='比較,比如邊緣端點? – user2357112

+2

你的例子有些東西不在這裏。這是蟒蛇3嗎?不能,你的'打印'將打印'地圖'對象,而不是列表。這是蟒蛇2嗎?不能,你的'打印'將打印元組。 – roippi

回答

2

心理調試:你在加入這個代碼開始之前,那麼在突變能改變方式node得到set其成員哈希值。 set在插入時緩存每個對象的哈希值,並且在正常情況下不會重新哈希;事實上,從set複製或更新到set也可以避免重新哈希,因爲它可以直接複製緩存的哈希值而不是重新哈希。

當您轉換爲list(它實際上剝離了緩存的哈希值),然後返回到set(它現在必須重新哈希所有元素,這會導致它修復重複)時,「修復」了問題。

當然,這不是一個真正的解決辦法。真正的解決方法是讓從不使可變項可哈希;遵循Python的指導原則,只使用不可變類型,或允許從可變類型轉換爲不可變類型,其中只有不可變類型定義爲__hash__。如果你定義了__hash__,那麼你就註冊了可哈希類型的規則,這些規則包括邏輯不可變性(以及哈希計算中涉及的所有屬性的不變性)。

+0

太棒了,謝謝。這聽起來是正確的。我知道集應該總是隻包含不可變的對象,但由於某種原因,並沒有連接成爲這個bug的原因。我會經歷並且只將'__hash__'放在不可變的對象上。謝謝 –