2017-08-03 74 views
0

我有一個形式爲for thing in a_set:的循環。它工作不正確,因爲偶爾和不一致地,它會從集合中拉出同樣的東西兩次。 (這不會導致程序崩潰,它只是得到了錯誤的答案。)我無法確定任何關於錯誤行爲的確定性;但我的調試嘗試很清楚,有時候會發生奇怪的事情。在我最密切觀察的情況下,組中有3個項目(前後)和循環執行4次,一次重複其中一個項目。這些項目是對我創建的類的對象的引用(更像C結構)。當我將for語句更改爲for thing in list(a_set):時,不良行爲就消失了。任何人都可以解釋這個奇怪的錯誤迭代集?

我完全喪失解釋錯誤行爲。我非常肯定,循環體中沒有任何東西可以導致它發生兩次或更改thing變量的值。我很確定循環中發生的事情不會影響組合的組合。此外,即使它可以,我相信會導致RuntimeError。對於可能造成這種情況的假設提出假設,我完全喪失了信心。連續運行相同代碼的缺乏可重複性特別神祕。我嘗試在一個更簡單的場景中重現症狀失敗了。儘管如此,爲了解決一個我無法解釋的問題,我覺得離開list()的調用很愚蠢。任何人的假設都會受到歡迎。我需要關於在調試時應該嘗試消除哪些事情的想法。

更新:我認爲這個問題被錯誤地擱置,基於聲稱它是脫離主題。在這種情況下缺乏重現性是個問題,我懷疑我錯過了一些語言的細微差別。的確,情況確實如此,MSeifert的回答讓我想起了導致它的原因。但是,這並不像他推測的那麼簡單,正如我在評論他的答案時所注意的那樣。

我也混淆了這個問題,說集合中的對象是可變的。他們不是。它們是對屬性可更改的對象的引用。 (這可以從我寫的內容中推斷出來,但是我在一般意義上不正確地使用「mutable」一詞,而不是Python技術意義上的術語。)哈希值是對象的地址,與其值無關屬性。如果這些對象引用是可變的,那麼Python永遠不會讓我把它們放在一個集合中。

+3

你將需要一些方法來重現這一點。你的套件是否有變化的可變對象? – matt

+0

@matt。這是缺乏再現性的問題。是的,集合中的對象是可變的。他們有屬性正在改變;但他們仍然是同一個對象。在不發生不良行爲的大多數情況下,這些屬性的變化也在發生(沒有列表)。 –

+0

你是如何創建你的設置? – matt

回答

6

如果在添加list(a_set)時錯誤消失,則很可能在迭代期間更改了該設置。一般來說,這將引發一個RuntimeError但如果你添加儘可能多的元素,你刪除它不會觸發:

a = {1,2,3} 
for item in a: 
    print(item) 
    a.add(item+3) # add one item 
    a.remove(item) # remove one item 

打印數量131(金額實際上是一個實現細節,所以你可能會看到不同量)以及在循環之前和之後以及在每次迭代開始時,set包含3元素。

但是如果我添加list調用它創建原始集的副本(如表),並只迭代是存在於原設定的元素:

a = {1,2,3} 
for item in list(a): 
    print(item) 
    a.add(item+3) 
    a.remove(item) 

print(a) 

打印:

1 
2 
3 
set([4, 5, 6]) # totally changed! 

在評論你指出,你在設置有類是可變的,所以即使你可能認爲你祛瘀e並添加相同的元素,它可能不再是相同的元素(從set的角度來看)。一般而言,您不應將可變類放入set或作爲dict中的鍵,因爲您必須非常小心可變性不會影響__hash____eq__方法的結果。

class Fun(object): 
    def __init__(self, value): 
     self.value = value 

    def __repr__(self): 
     return '{self.__class__.__name__}({self.value})'.format(self=self) 

    def __eq__(self, other): 
     return self.value == other.value 

a = {Fun(1),Fun(2),Fun(3)} 
for item in a: 
    print(item) 
    a.add(Fun(item.value+3)) 
    a.remove(item) 

實際上將顯示一個「隨機」(不是真的隨機它只是取決於實例,並在這個散列:

只是,在元素集合中的看似「隨機」數迭代的例子情況下,散列取決於類對象的id,每當我運行代碼時,每次運行代碼時都會更改這個類對象的數量)Fun對象。

+1

你的答案使我能夠弄清楚出了什麼問題。這是一個遞歸回溯計劃。事實證明,該套件正在循環中修改;但是在循環的下一次迭代之前,回溯過程總是將集合的內容放回到從一開始就在那裏的非常相同的一組對象。也就是說,雖然它正在移除一個對象,但它卻把完全相同的對象放回去了。在我看來,這一組並沒有改變。然而,刪除/添加仍然顯然是爲了讓迭代器「正確地」行爲太多而繞過集​​合。非常感謝。 –

相關問題