それを繰り返しながらPythonの辞書から項目を削除することは合法的ですか?
例えば:
for k, v in mydict.iteritems():
if k == val:
del mydict[k]
アイデアは、反復されているもののサブセットである新しい辞書を作成する代わりに、辞書から特定の条件を満たさない要素を削除することです。
これは良い解決策ですか?もっとエレガントで効率的な方法はありますか?
編集:
この答えは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}
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)
繰り返しながらコレクションを修正することはできません。その方法は狂気 - あなたが現在のアイテムを削除して削除することを許可されている場合、イテレータは(+1)に進み、次のnext
の呼び出しはそれを超えてあなたを連れて行くでしょう。 d 1つの要素(削除した要素のすぐ後ろの要素)をスキップすることになります。 2つの選択肢があります。
.keys()
などを使用できます(Python 3では、結果のイテレータをlist
に渡します)。ただし、スペース的に非常に無駄があります。to_delete
に保存して、通常どおりmydict
を繰り返します。 mydict
の繰り返しが終わったら、mydict
からto_delete
内のすべての項目を削除します。最初の方法よりもいくつかのスペースを節約します(削除されるキーの数と残りの数によって異なります)が、さらに数行必要になります。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のオーバーロードをせずに他の辞書(最初の辞書の修正を含む)を作成したいときに使用します。
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で動作します。
あなたは辞書の内包表記を使うことができます。
d = {k:d[k] for k in d if d[k] != val}
最初に削除するキーのリストを作成してから、そのリストを反復して削除します。
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]
私はPython 3で上記の解決策を試してみましたが、これがオブジェクトを辞書に格納するときに私には有効な唯一の解決策のようです。基本的にあなたはあなたのdict()のコピーを作成し、あなたのオリジナルの辞書の中のエントリを削除しながらそれを繰り返す。
tmpDict = realDict.copy()
for key, value in tmpDict.items():
if value:
del(realDict[key])
あなたが削除したい項目が常に辞書の繰り返しの "先頭"にある場合に適しているかもしれない方法があります。
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では、他の多くの答えが示唆している辞書のコピーを避けることができます。