2014-11-21 56 views
1

我有一段python代碼,它需要遍歷字典中循環體可能會更改字典的所有鍵的所有鍵。在改變的字典上迭代

只是試圖迭代字典給我RuntimeError: dictionary changed size during iteration。搜索錯誤消息導致我到another question,其中建議的解決方案是在開始迭代之前複製密鑰列表。

在我的場景中,不過只有部分答案,因爲我需要迭代不僅原始鍵,而且遍及我迭代時添加的鍵。我需要的代碼只有在沒有更多的鍵被添加到字典時纔會終止,並且它已經處理了字典中已有的任何鍵。

到目前爲止,我想出了這一點:

processed = set() 
while True: 
    keys = set(dictionary) - processed 
    if not keys: 
     break 
    for k in keys: 
     processed.add(k) 
     # Do something with k 

這種做法似乎過於複雜。有什麼辦法可以簡化它,並且仍然可以處理所有添加到字典中的密鑰,而不必多次處理它們中的每一個?

在while循環的前四行可能已被簡單地寫成其他語言:

while keys = set(dictionary) - processed: 

但是這並不在Python工作。我發現a question關於在python的while循環中使用賦值作爲條件。但是,沒有一個建議的答案似乎適用於我的情況。

回答

0

我重寫我的代碼:

processed = set() 
while set(dictionary) - processed: 
    for k in set(dictionary) - processed: 
     processed.add(k) 
     # Do something with k 

這並複製set(dictionary) - processed表達,但仍使代碼更易於閱讀。

2

考慮相反的方法。跟蹤仍然需要處理的密鑰,而不是已經存在的密鑰。

remaining = set(dictionary.items()) 

while remaining: 
    key, value = remaining.pop() 

    # process the item 

    if item_to_add: 
     dictionary[new_key] = new_value 
     remaining.add((new_key, new_value)) 

這避免了每次迭代的昂貴的集合差異操作。

如果你真的不知道在處理過程中添加了什麼鍵,那麼你的問題中的代碼是好的。寫一個稍微不同的方法是:

keys = set(dictionary) 
processed = set() 

while keys: 
    for k in keys: 
     # Do something with k 

    processed.update(keys) 
    keys = set(dictionary) - processed 

這樣更好嗎?更差?我想你決定。

+0

我的循環體沒有明確地向字典添加鍵。相反,它調用另一個函數,它可能會或可能不會將鍵添加到字典中。因此,我的循環體可以知道哪些鍵已被添加的唯一方法是通過查看當前的一組鍵。 – kasperd 2014-11-21 03:58:14

+0

重複檢查字典以查看更改的內容可能很昂貴。有什麼方法可以重構代碼,以便知道添加了哪些項目?如果沒有,那麼你的原代碼對我來說看起來不錯。 – 2014-11-21 04:00:40

+0

爲了讓循環的主體知道正在添加的內容,重構代碼並不實際。在我的情況下,設置差異的性能不會成爲問題,因爲處理單個密鑰的成本遠高於計算設置差異的成本。 – kasperd 2014-11-21 04:04:34

2

也許你可以用OrderedDict來代替。在迭代時添加鍵沒有問題

>>> from collections import OrderedDict 
>>> d = OrderedDict([(1, 2), (3, 4)]) 
>>> for k in d: 
...  if k == 1:d[5] = 6 
...  print(k) 
... 
1 
3 
5 

您是更新密鑰還是隻添加它們?如果更新,你需要指定爲是否關鍵應再次或不處理

+0

這聽起來像它可能是一個更乾淨的解決方案。文檔指出迭代將按照添加順序處理鍵。但是,它並沒有說明迭代過程中添加的鍵是否保證在迭代完成之前被處理。我不更改現有的密鑰,我只添加和刪除密鑰。 – kasperd 2014-11-21 04:25:42