1. ホーム
  2. python

リストから辞書を削除する

2023-08-03 16:29:34

質問

もし私が辞書のリストを持っているとしたら、例えば。

[{'id': 1, 'name': 'paul'},
 {'id': 2, 'name': 'john'}]

で、辞書を削除したいのですが id の2つ(または名前 'john' を含む)、プログラム的にこれを行う最も効率的な方法は何ですか(つまり、私はリスト内のエントリのインデックスを知らないので、単純にポップすることはできません)。

どのように解決するのですか?

thelist[:] = [d for d in thelist if d.get('id') != 2]

編集

このコードの性能について、コメントでいくつかの疑問が示されているので (Python の性能特性に対する誤解に基づくものと、キー 'id' の値が 2 であるリスト内の dict がちょうど 1 つであると与えられた仕様以上に仮定することによるもの)、この点について安心させてあげたいと思います。

古いLinuxボックスで、このコードを測定します。

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(99)]; import random" "thelist=list(lod); random.shuffle(thelist); thelist[:] = [d for d in thelist if d.get('id') != 2]"
10000 loops, best of 3: 82.3 usec per loop

のうち、random.shuffleに約57マイクロ秒(削除する要素が常に同じ場所にないことを保証するために必要;-)、最初のコピーに0.65マイクロ秒(Pythonリストの浅いコピーによるパフォーマンスの影響を心配している人は、明らかに昼食を取っていない;-)、ループ内の元のリストを変更しないために必要(従ってループの各脚には、削除する何かがある;-)です。

削除するちょうど1つの項目があることが分かっているとき、それを見つけてより迅速に削除することが可能です。

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(99)]; import random" "thelist=list(lod); random.shuffle(thelist); where=(i for i,d in enumerate(thelist) if d.get('id')==2).next(); del thelist[where]"
10000 loops, best of 3: 72.8 usec per loop

(を使用します。 next ではなく、組み込みの .next メソッドではなく) -- しかしこのコードは、削除条件を満たすディクショ ンの数がちょうど1でない場合に破綻します。これを一般化すると、次のようになります。

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*3; import random" "thelist=list(lod); where=[i for i,d in enumerate(thelist) if d.get('id')==2]; where.reverse()" "for i in where: del thelist[i]"
10000 loops, best of 3: 23.7 usec per loop

ここで、シャッフルは削除することができます。なぜなら、すでに知っているように、削除すべき3つの等位なディクショがあるからです。そして、listcompは、変更されずに、うまくいきます。

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*3; import random" "thelist=list(lod); thelist[:] = [d for d in thelist if d.get('id') != 2]"
10000 loops, best of 3: 23.8 usec per loop

は、99のうちたった3つの要素を削除するだけで、完全に首尾一貫しています。より長いリストとより多くの繰り返しで、これはもちろんさらに保持されます。

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*133; import random" "thelist=list(lod); where=[i for i,d in enumerate(thelist) if d.get('id')==2]; where.reverse()" "for i in where: del thelist[i]"
1000 loops, best of 3: 1.11 msec per loop
$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*133; import random" "thelist=list(lod); thelist[:] = [d for d in thelist if d.get('id') != 2]"
1000 loops, best of 3: 998 usec per loop

全体として、完全に単純で明白なリスト内包に対して、削除するインデックスのリストを作成し反転させるという繊細さを展開する価値は、1 つの小さなケースで 100 ナノ秒を獲得し、より大きなケースで 113 マイクロ秒を失う可能性が明らかにあります;-) 。シンプルでわかりやすく、完全にパフォーマンスが適切なソリューション (この一般的なクラスの "remove some items from a list" 問題に対するリスト理解など) を回避または批判することは、「早すぎる最適化はプログラミングにおけるすべての悪の根源である」という Knuth と Hoare の有名なテーゼの特にひどい例と言えるでしょう(quot; -quot;)。