「RuntimeError:ディクショナリのサイズが反復中に変更されました」エラーを回避する方法


258

同じエラーで他のすべての質問を確認しましたが、役立つ解決策は見つかりませんでした= /

リストの辞書があります:

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

一部の値は空です。これらのリストの作成の最後に、辞書を返す前にこれらの空のリストを削除したいと思います。現在私はこれを次のようにしようとしています:

for i in d:
    if not d[i]:
        d.pop(i)

ただし、これにより実行時エラーが発生します。辞書を反復処理している間は、辞書の要素を追加/削除できないことを知っています。その場合、これを回避する方法は何でしょうか?

回答:


455

Python 2.xでは、呼び出しkeysによってキーのコピーが作成されます。これを変更するときに繰り返し使用できますdict

for i in d.keys():

keysリストの代わりにイテレータを返すため、これはPython 3.xでは機能しないことに注意してください。

別の方法は、を使用listしてキーのコピーを強制的に作成することです。これはPython 3.xでも機能します。

for i in list(d):

1
私はあなたが「呼び出しkeysはあなたが繰り返すことができるキーのコピーを作る」ことを意味していたと思いますpluralか?そうでなければ、どのようにして単一のキーを反復できますか?ちなみに私は単なるピッキングではなく、それが本当にキーであるかどうかを本当に知りたいと思っています
HighOnMeat

6
または、リストの代わりにタプルの方が高速です。
Brambor 2018

8
python 3.xのの行動を明確にするため、d.keys()を返す反復可能な(ないイテレータを)それが直接、辞書のキーにビューのことを意味します。を使用すると、for i in d.keys()実際にはpython 3.x でも一般的に機能しますが、辞書のキーの反復可能なビューを反復処理するためd.pop()、ループ中に呼び出すと、同じエラーが発生します。for i in list(d)あなたのような特別な状況で、反復する前にキーをリストにコピーする、少し非効率的なpython 2の動作をエミュレートします。
マイケルクレブス

内部dictでオブジェクトを削除する場合、python3ソリューションは機能しません。たとえば、辞書Aと辞書Aに辞書Bがあるとします。辞書Bのオブジェクトを削除する場合は、エラーが発生します
Ali-T

1
python3.xではlist(d.keys())、と同じ出力を作成し、aをlist(d)呼び出すとキーが返されます。コールは、(高価ではないことが)不要です。listdictkeys
Sean Breckenridge

46

辞書内包表記を使用して、関連するアイテムを新しい辞書にコピーするだけです

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.iteritems() if v}
>>> d
{'a': [1], 'b': [1, 2]}

これのためにPython 3で

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.items() if v}
>>> d
{'a': [1], 'b': [1, 2]}

11
d.iteritems()エラーが出ました。私はd.items()代わりに使用しました-python3 を使用して
wcyn

4
これは、OPの質問で提起された問題に有効です。ただし、マルチスレッドコードでこのRuntimeErrorが発生した後にここに来た人は誰でも、CPythonのGILがリスト内包の途中で解放される可能性があるため、別の方法で修正する必要があることに注意してください。
Yirkha

40

「コピー」のみを使用する必要があります。

その方法で、元のディクショナリフィールドを反復処理し、オンザフライで目的の辞書(d dict)を変更できます。各pythonバージョンで機能するため、より明確になります。

In [1]: d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

In [2]: for i in d.copy():
   ...:     if not d[i]:
   ...:         d.pop(i)
   ...:         

In [3]: d
Out[3]: {'a': [1], 'b': [1, 2]}

働きました!ありがとうございました。
Guilherme Carvalho Lithg

13

最初は空のリストを挿入しないようにしますが、通常は次のようにします。

d = {k: v for k,v in d.iteritems() if v} # re-bind to non-empty

2.7より前の場合:

d = dict( (k, v) for k,v in d.iteritems() if v )

あるいは単に:

empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
    del[k]

+1:最後のオプションは、削除が必要なアイテムのキーのみをコピーするため、興味深いものです。これは、dictのサイズに比べて削除する必要があるアイテムの数が少ない場合に、パフォーマンスが向上する可能性があります。
Mark Byers、2012

@MarkByers yup-そして、多数の場合、フィルタリングされた新しい辞書にdictを再バインドすることをお勧めします。それは常に構造がどのように機能するかについての期待です
Jon Clements

4
再バインドの1つの危険は、プログラム内のどこかに、古い辞書への参照を保持するオブジェクトがあった場合、変更が表示されないことです。そうでないことが確かな場合は、確かに...それは合理的なアプローチですが、元の辞書を変更することとはまったく同じではないことを理解することが重要です。
Mark Byers、2012

@MarkByersは非常に良い点です-あなたと私はそれを知っています(他にも数え切れないほどです)が、それはすべてに明らかではありません。そして、私はそれが後ろのあなたをかまなかったテーブルにお金を置きます:)
ジョン・クレメンツ

空のエントリを挿入しないようにする点は非常に良いものです。
Magnus Bodin

12

Python 3の場合:

{k:v for k,v in d.items() if v}

素敵で簡潔です。Python 2.7でも動作しました。
donrondadon

6

forループ中に辞書が変更されている間、辞書を反復処理することはできません。リストにキャストして、そのリストを反復処理します。それは私にとってはうまくいきます。

    for key in list(d):
        if not d[key]: 
            d.pop(key)

1

このような状況では、深いコピーを作成し、元の辞書を変更しながらそのコピーをループ処理します。

ルックアップフィールドがリスト内にある場合、リストのforループで列挙し、位置をインデックスとして指定して、元の辞書のフィールドにアクセスできます。


1

これは私のために働きました:

dict = {1: 'a', 2: '', 3: 'b', 4: '', 5: '', 6: 'c'}
for key, value in list(dict.items()):
    if (value == ''):
        del dict[key]
print(dict)
# dict = {1: 'a', 3: 'b', 6: 'c'}  

辞書の項目をリストにキャストすると、その項目のリストが作成されるため、それを反復してを回避できますRuntimeError


1

dictc = {"stName": "asas"} keys = dictc.keys()for key in list(keys):dictc [key.upper()] = '新しい値' print(str(dictc))


0

実行時エラーの理由は、反復中に構造が変化している間はデータ構造を反復できないためです。

あなたが探しているものを達成する1つの方法は、リストを使用して削除したいキーを追加し、次にリストの反復中に特定のキーを削除するために辞書のポップ機能を使用することです。

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
pop_list = []

for i in d:
        if not d[i]:
                pop_list.append(i)

for x in pop_list:
        d.pop(x)
print (d)

0

Python 3では、(上記のforループを使用して)ディクショナリを反復している間は削除できません。さまざまな代替案があります。簡単な方法の1つは、次の行を変更することです

for i in x.keys():

for i in list(x)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.