2017-06-26 8 views
6

ディクショナリを反復処理中に変更します。 Python dictのバグ?

d = {1: 1} 
for k in d.keys(): 
    d['{}'.format(k)] = d.pop(k) 
print(d) 

の出力は{'1': 1}です。

d = {1: 1} 
for k in d.keys(): 
    d['i{}'.format(k)] = d.pop(k) 
print(d) 

の出力は{'iiiii1': 1}です。これはバグですか?私はPython 3.6.1 :: Anaconda 4.4.0 (x86_64)を実行しています。

+9

なぜそれがバグでしょうか?反復処理中*キーを削除して追加します。あなたは無限ループに終わらなかったのは幸運です。 –

+3

*繰り返しながら*コレクションを変更*しないでください。それは奇妙な行動につながる可能性があるので、特に辞書ではありません。 –

+0

@ user2725109:最初のループは一度だけ実行されたことをどのように知っていますか?あなたが知っているすべてのために、それは1000回走った。 –

答えて

13

いいえ、これはバグではありません。これが事実explicitly documentedである:

キーと値が非ランダムである任意の順序で繰り返し処理され、Pythonの実装で変化し、挿入および欠失の辞書の歴史に依存します。キー、値、およびアイテムビューが辞書に介入することなく反復される場合、アイテムの順序は直接対応します。

[...]辞書のエントリを追加または削除するとRuntimeErrorを上げるか、すべてのエントリを反復処理に失敗することがありながら景色を反復

太字強調。

同時に、辞書のエントリを追加したり削除したりしながら、キーを反復処理しています。それはいくつかの反復のために働いていたし、すべてのケースと反復を停止して、を反復することに失敗しました。

何が起こるかは、6回の追加で再サイズをトリガし、その時点で反復が失敗することです。 「次の」キーは「以前の」スロットにスロットされています。これは両方のテストのために起こる、あなたはそれが両方のケースで5回反復し理解していない:現在のdict実装はhash table of size 8で始まるので、それが5回実行され

>>> d = {1: 1} 
>>> for i, k in enumerate(d): 
...  print(i) 
...  d['{}'.format(k)] = d.pop(k) 
... 
0 
1 
2 
3 
4 
>>> d = {1: 1} 
>>> for i, k in enumerate(d): 
...  print(i) 
...  d['i{}'.format(k)] = d.pop(k) 
... 
0 
1 
2 
3 
4 

、およびサイズ変更はwhen the table is 2/3s fullを引き起こしています(ハッシュコリジョンを正しく処理するために必要な)キーを削除すると、テーブルにDKIX_DUMMY entitiesが入力されています。

これはすべて実装依存性が高いことに注意してください。 Python 3.5以前では、両方のスニペットが一度(キーのリストオブジェクトの作成を避けるためにfor k in d:を使用する場合でも)。インプリメンテーションが変更され、イテレーションが挿入順に続いているため、3.6でイテレーションが続行されます。 Pythonの将来のバージョンでは、実装を改めて自由に変更できます。

関連する問題