web-dev-qa-db-ja.com

それを反復しながら辞書から項目を削除する方法?

それを繰り返しながらPythonの辞書から項目を削除することは合法的ですか?

例えば:

for k, v in mydict.iteritems():
   if k == val:
     del mydict[k]

アイデアは、反復されているもののサブセットである新しい辞書を作成する代わりに、辞書から特定の条件を満たさない要素を削除することです。

これは良い解決策ですか?もっとエレガントで効率的な方法はありますか?

249
user248237

編集:

この答えはPython 3では機能せず、RuntimeErrorとなります。

RuntimeError:辞書は繰り返し中にサイズが変更されました。

これは、mydict.keys()がリストではなくイテレータを返すために起こります。コメントで指摘されているように、単にmydict.keys()によってlist(mydict.keys())をリストに変換すればうまくいくはずです。


コンソールの簡単なテストでは、辞書を繰り返し処理している間は辞書を変更できないことがわかります。

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k, v in mydict.iteritems():
...    if k == 'two':
...        del mydict[k]
...
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

Delnanの答えで述べたように、エントリを削除すると、反復子が次のエントリに移動しようとしたときに問題が発生します。代わりに、keys()メソッドを使用してキーのリストを取得し、それを処理してください。

>>> for k in mydict.keys():
...    if k == 'two':
...        del mydict[k]
...
>>> mydict
{'four': 4, 'three': 3, 'one': 1}

Itemsの値に基づいて削除する必要がある場合は、代わりにitems()メソッドを使用してください。

>>> for k, v in mydict.items():
...     if v == 3:
...         del mydict[k]
...
>>> mydict
{'four': 4, 'one': 1}
277
Blair

2つのステップでそれをすることもできます:

remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]

私のお気に入りのアプローチは、通常新しい辞書を作ることです。

# Python 2.7 and 3.x
mydict = { k:v for k,v in mydict.items() if k!=val }
# before Python 2.7
mydict = dict((k,v) for k,v in mydict.iteritems() if k!=val)
78
Jochen Ritzel

繰り返しながらコレクションを修正することはできません。その方法は狂気 - あなたが現在のアイテムを削除して削除することを許可されている場合、イテレータは(+1)に進み、次のnextの呼び出しはそれを超えてあなたを連れて行くでしょう。 d 1つの要素(削除した要素のすぐ後ろの要素)をスキップすることになります。 2つの選択肢があります。

  • すべてのキー(または必要に応じて値、またはその両方)をコピーしてから、それらを繰り返します。これには.keys()などを使用できます(Python 3では、結果のイテレータをlistに渡します)。ただし、スペース的に非常に無駄があります。
  • 削除するキーを別のコレクションto_deleteに保存して、通常どおりmydictを繰り返します。 mydictの繰り返しが終わったら、mydictからto_delete内のすべての項目を削除します。最初の方法よりもいくつかのスペースを節約します(削除されるキーの数と残りの数によって異なります)が、さらに数行必要になります。
18
user395760

items()によって返されるように、代わりにコピーを繰り返します。

for k, v in list(mydict.items()):

Python3では、dic.keys()を繰り返すと辞書サイズのエラーが発生します。あなたはこの代替方法を使用することができます:

Python3でテストされて、それはうまく働きます、そして、エラー「繰り返しの間に辞書はサイズを変えました」は起こされません:

my_dic = { 1:10, 2:20, 3:30 }
# Is important here to cast because ".keys()" method returns a dict_keys object.
key_list = list( my_dic.keys() )

# Iterate on the list:
for k in key_list:
    print(key_list)
    print(my_dic)
    del( my_dic[k] )


print( my_dic )
# {}

たくさんのメモリを使用している辞書から、「コピー」やRAMのオーバーロードをせずに他の辞書(最初の辞書の修正を含む)を作成したいときに使用します。

8
glihm

list(mydict)を使うのが最もきれいです。

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k in list(mydict):
...     if k == 'three':
...         del mydict[k]
... 
>>> mydict
{'four': 4, 'two': 2, 'one': 1}

これはリストの並列構造に対応します。

>>> mylist = ['one', 'two', 'three', 'four']
>>> for k in list(mylist):                            # or mylist[:]
...     if k == 'three':
...         mylist.remove(k)
... 
>>> mylist
['one', 'two', 'four']

どちらもpython2とpython3で動作します。

6
rsanden

あなたは辞書の内包表記を使うことができます。

d = {k:d[k] for k in d if d[k] != val}

5
Aaron

最初に削除するキーのリストを作成してから、そのリストを反復して削除します。

dict = {'one' : 1, 'two' : 2, 'three' : 3, 'four' : 4}
delete = []
for k,v in dict.items():
    if v%2 == 1:
        delete.append(k)
for i in delete:
    del dict[i]
4
Pob

私はPython 3で上記の解決策を試してみましたが、これがオブジェクトを辞書に格納するときに私には有効な唯一の解決策のようです。基本的にあなたはあなたのdict()のコピーを作成し、あなたのオリジナルの辞書の中のエントリを削除しながらそれを繰り返す。

        tmpDict = realDict.copy()
        for key, value in tmpDict.items():
            if value:
                del(realDict[key])
1
JLandbrug

あなたが削除したい項目が常に辞書の繰り返しの "先頭"にある場合に適しているかもしれない方法があります。

while mydict:
    key, value = next(iter(mydict.items()))
    if should_delete(key, value):
       del mydict[key]
    else:
       break

「始まり」は、特定のPythonのバージョン/実装に対して一貫していることだけが保証されています。たとえば、 Python 3.7の新機能 などです。

辞書オブジェクトの挿入順序の保存性は、Python言語仕様の公式部分であると宣言されています。

このようにして、少なくともPython 3では、他の多くの答えが示唆している辞書のコピーを避けることができます。

1
Michal Charemza