繰り返しながらPythonの辞書から項目を削除することは正当ですか?
例えば:
for k, v in mydict.iteritems():
if k == val:
del mydict[k]
アイデアは、繰り返される辞書のサブセットである新しい辞書を作成する代わりに、特定の条件を満たさない要素を辞書から削除することです。
これは良い解決策ですか?よりエレガントで効率的な方法はありますか?
繰り返しながらPythonの辞書から項目を削除することは正当ですか?
例えば:
for k, v in mydict.iteritems():
if k == val:
del mydict[k]
アイデアは、繰り返される辞書のサブセットである新しい辞書を作成する代わりに、特定の条件を満たさない要素を辞書から削除することです。
これは良い解決策ですか?よりエレガントで効率的な方法はありますか?
回答:
編集:
この答えはPython3では機能せず、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}
for k, v in list(mydict.items()):
Python 3で正常に動作するように見せることにkeys()
なりlist(keys())
ます。
RuntimeError: dictionary changed size during iteration
for k in list(mydict.keys()):
、keys()メソッドをイテレータにし、反復中にdict項目を削除することもできません。list()呼び出しを追加することにより、keys()イテレータをリストに変換します。したがって、forループの本体にいるときは、辞書自体を繰り返し処理する必要はありません。
次の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)
remove
ループアプローチの方が好きです。
for k in [k for k in mydict if k == val]: del mydict[k]
反復中にコレクションを変更することはできません。その方法は狂気です-特に、現在のアイテムを削除して削除することが許可されている場合、イテレータは(+1)に移動する必要があり、次のへの呼び出しnext
はそれを超えて(+2)するため、 1つの要素(削除した要素のすぐ後ろの要素)をスキップすることになります。次の2つのオプションがあります。
.keys()
これにはet alを使用できます(Python 3では、結果のイテレーターをに渡しますlist
)。スペース的には非常に無駄が多いかもしれません。mydict
通常どおり繰り返し、削除するキーを別のコレクションに保存しますto_delete
。反復が完了したらmydict
、to_delete
からすべてのアイテムを削除しますmydict
。最初のアプローチよりも(削除されたキーの数と滞在数に応じて)スペースをいくらか節約しますが、さらに数行を必要とします。You can't modify a collection while iterating it.
これはdictsや友人のためにちょうど正しいですが、反復中にリストを変更することができますL = [1,2,None,4,5] <\n> for n,x in enumerate(L): <\n\t> if x is None: del L[n]
can't
はshouldn't
リストのためであるべきですが、それでも辞書と友達のためだけに正しいです。
代わりに、によって返されたコピーなどのコピーを反復処理しますitems()
。
for k, v in list(mydict.items()):
del v
直接することはできないため、決して使用しない各vのコピーを作成しており、いずれにしてもキーで項目にアクセスする必要があります。dict.keys()
より良い選択です。
v
、削除の基準としてどれだけの量を使用する必要があるかに依存します。
使用するのが最もきれいです 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で動作します。
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 )
# {}
最初に削除するキーのリストを作成し、次にそのリストを繰り返して削除します。
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]
削除したいアイテムが常にdict反復の「開始」にある場合に適した方法があります
while mydict:
key, value = next(iter(mydict.items()))
if should_delete(key, value):
del mydict[key]
else:
break
「最初」は、特定のPythonバージョン/実装でのみ一貫していることが保証されています。たとえば、Python 3.7の新機能から
dictオブジェクトの挿入順保持の性質は、Python言語仕様の公式の一部であると宣言されています。
この方法により、他の多くの回答が示唆している、少なくともPython 3では、口述のコピーを回避できます。
私は上記の解決策をPython3で試しましたが、dictにオブジェクトを格納するときに私に役立つ唯一の解決策のようです。基本的には、dict()のコピーを作成し、元の辞書のエントリを削除しながらそれを繰り返します。
tmpDict = realDict.copy()
for key, value in tmpDict.items():
if value:
del(realDict[key])