辞書から要素を削除する


1395

Pythonの辞書から項目を削除する方法はありますか?

さらに、コピーを返す(つまり、元のアイテムを変更しない)ために、辞書からアイテムを削除するにはどうすればよいですか?


13
辞書を直接変更できるのに、なぜ辞書を返す関数が必要なのですか?
amillerrhodes

5
辞書pop方法は、辞書を変更するには、場所。したがって、呼び出し元から「ヘルパー関数」に渡された辞書への参照を変更します。したがって、呼び出し元の辞書への元の参照はすでに変更されているため、「ヘルパー関数」は何も返す必要はありません。dict.pop()必要がない場合は、どこからの戻り値も割り当てないでください。EG: do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_keydeepcopy(my_dict)必要に応じて使用してください。
Mark Mikofski 16

1
元のタイトルは詳細に同意せず、明白な解決策を明確に除外したのでd.pop()、詳細で指定された質問をするようにタイトルを修正しました。
smci

1
私たちは、あなたがどうかを尋ねる警告を追加する必要があり、実際にこれをしたいあなたはE要素を持つ辞書にN回それを行うかのように、あなたはすべての深いコピーを(/使用)O(N * E)のメモリリークが発生しますが。読み取り専用(浅いコピー)だけが必要な場合は、を実行してくださいd.pop(key)。ただし、浅いコピーを変更する場合は、エイリアシングによく知られている問題があります。より広いコンテキストを教えてください。(他にdict値を変更することはありますか?リストを破壊的に反復しようとしていますか?そうでない場合は、何ですか?)
smci

5
「ディクショナリを直接変更できるのに、なぜディクショナリを返す関数が必要なのですか?」おそらく、パラメータを変更しない純粋な関数を記述したいからでしょうか?
Gene Callahan

回答:


1728

次のdelステートメントは要素削除します。

del d[key]

ただし、これにより既存のディクショナリが変更されるため、同じインスタンスへの参照を持っている他の人のためにディクショナリの内容が変更されます。新しい辞書を返すには、辞書のコピーを作成します。

def removekey(d, key):
    r = dict(d)
    del r[key]
    return r

dict()コンストラクタは作る浅いコピーを。詳細なコピーを作成するには、copyモジュールを参照してください。


すべてのdict del/ assignment / etcのコピーを作成することに注意してください。は、一定の時間から線形の時間に移行し、線形空間を使用することも意味します。小さな辞書の場合、これは問題ではありません。ただし、大規模な辞書のコピーを多数作成する予定の場合は、(この回答で説明されているように)HAMTのような別のデータ構造がおそらく必要です。


15
これは、辞書の可変性+1についての重要な点です。辞書のコピーが必要だったときは思いつきませんが、「全員」のコピーが同じであることに常に依存してきました。素晴らしい点。
tMC 2011

30
あなたが編集する場合@tMCは、dictあなたがそれをループしているように、それはあなたのエラーをあげる:RuntimeError: dictionary changed size during iteration
VertigoRay

15
pop実際に同じことをする方法はどうですか?もっとpythonicではないですか?(特別な予約語ではなく、dictのメソッド)?
セルジュ

21
この回答には弱点があり、誤解を招く可能性があります。読者は、dict(d)が 'd'付きのコピーを提供できると誤解しているかもしれません。しかし、それは不完全なコピーです。Delキー操作のみを行う場合は、それで問題ありません。しかし、入れ子になったdictに対して何か他のことをしたい場合、そのコピーメソッドを使用して 'r'を変更すると、元の 'd'が変更される可能性があります。本物のコピーを取得するには、最初に「import copy」、次に「r = copy.deepcopy(d)」が必要です。
Zen

10
@禅:十分に公平です。浅いコピーと深いコピーについてのメモを追加しました。
グレッグヒューギル2014

264

pop 辞書を変更します。

 >>> lol = {"hello": "gdbye"}
 >>> lol.pop("hello")
     'gdbye'
 >>> lol
     {}

オリジナルを保持したい場合は、コピーしてください。


29
「デル」は大丈夫ですが、私の意見では「ポップ」はより「Pythonic」のようです。
ivanleoncz

1
@ivanleonczなんで?
kevr 2018年

3
pop「ポップ」された値を返します。これにより、他の理由でこの値を使用できます。それがより「Pythonic」でないなら、確かにそれはより良いように見えると思います:)。それは辞書ではないが、それはどちらも同じように動作します:github.com/ivanlmj/python-prototypes/blob/master/3.4/...
ivanleoncz

2
@ivanleonczまた、もう1つの理由により優れています。dictにpopキーが欠落しているときに返されるデフォルト値を指定できます。いくつかのキーを削除する必要がある場合に適していますが、一部のキーが欠落している可能性があります。そのような場合にdelスローさKeyErrorれます。
イタチ

80

私はあなたの解決策がそれを行うための最良の方法だと思います。ただし、別の解決策が必要な場合は、次のように、指定したキーを含めずに、古い辞書のキーを使用して新しい辞書を作成できます。

>>> a
{0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> {i:a[i] for i in a if i!=0}
{1: 'one', 2: 'two', 3: 'three'}

2
すごくかっこいい。新しい関数を定義せずに辞書をフィルタリングする簡単な方法が好きです。
Joe J

6
理解に不慣れな人のために、次のようなこともでき{i:a[i] for i in a if i not in [0, 1, 2]}ます:複数の要素を削除したい場合。
kmatheny 2015

9
良いと{k:v for k,v in a.items() if k != 0}思います。
rlbond 2017

1
キーで項目を削除し、同じ行に新しい辞書の結果を返すための最良のソリューション。たとえば、あなたは、のような単一の項目なしで、すでに構築さ辞書を使用する必要がある場合**kwargssome_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
コール

ここでの最善の解決策。ライナーは1つで、元の辞書は変更されません。
Andrew Winterbotham

55

デルのステートメントは、あなたが探しているものです。「bar」というキーを持つfooという名前の辞書がある場合、次のようにfooから「bar」を削除できます。

del foo['bar']

これにより、操作中の辞書が永久に変更されることに注意してください。元の辞書を保持したい場合は、事前にコピーを作成する必要があります。

>>> foo = {'bar': 'baz'}
>>> fu = dict(foo)
>>> del foo['bar']
>>> print foo
{}
>>> print fu
{'bar': 'baz'}

dictコールは浅いのコピーを作成します。詳細なコピーが必要な場合は、を使用してくださいcopy.deepcopy

便宜上、コピーして貼り付けることができる方法は次のとおりです。

def minus_key(key, dictionary):
    shallow_copy = dict(dictionary)
    del shallow_copy[key]
    return shallow_copy

1
@ pythonian29033、実際には、いいえ。受け入れられた回答は期待どおりに機能します。1つのキーなしで辞書を返します。この回答からのアプローチは元の
口述を変更し

1
@ arussell84、なぜ>>>python-examplesでよく使用されるのですか?ええ、python-docにはそのようなものがたくさん含まれています。しかし、そのようなコードはcopypasteには不便です。私は混乱しています...
maxkoryukov

@maxkoryukovうんうん!ただし、その関数とこの答えはまったく同じです。ただし、関数内にその答えがある点が異なります。しばらくの間pythonでコーディングしていないはずですが>>>、cliモードのpythonからのリスニング表記を模倣しています
pythonian29033

2
@についてpythonian29033 >>>。はい、REPLスタイルですが、率直に話しましょう。このサンプルを書いたのは1人だけで、1000人がこれを読んでいます。簡単にコピーして実行できるように例を書くのは素晴らしいことだと思います。この山かっこを手で外したくありません。または、行ごとにコピーします。だから私は理解できません。なぜこの角度がまだそこにあるのか)))何かを知らないのではないですか?
maxkoryukov

3
便宜上、コピー/貼り付けできる機能を追加しました。
arussell84

48

良い答えはたくさんありますが、一つだけ強調したいと思います。

dict.pop()メソッドとより一般的なdelステートメントの両方を使用して、ディクショナリからアイテムを削除できます。どちらも元の辞書を変更するため、コピーを作成する必要があります(以下の詳細を参照)。

そして、それらの両方はKeyError、あなたが彼らに提供しているキーが辞書に存在しない場合に発生します:

key_to_remove = "c"
d = {"a": 1, "b": 2}
del d[key_to_remove]  # Raises `KeyError: 'c'`

そして

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove)  # Raises `KeyError: 'c'`

これに注意する必要があります:

例外をキャプチャすることにより:

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    del d[key_to_remove]
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

そして

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    d.pop(key_to_remove)
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

チェックを実行することにより:

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    del d[key_to_remove]

そして

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    d.pop(key_to_remove)

しかしpop()、はるかに簡潔な方法もあります-デフォルトの戻り値を提供します:

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove, None)  # No `KeyError` here

pop()削除されるキーの値を取得するために使用しない限り、必要ではないものを提供できますNone。独自の複雑な機能を備えた関数であるためdelincheck を使用した方が若干高速になる可能性がありますが、pop()オーバーヘッドが発生します。通常はそうではないのでpop()、デフォルト値で十分です。


主な質問については、元の辞書を保存し、キーを削除せずに新しい辞書を作成するには、辞書のコピーを作成する必要があります。

他の一部の人は、で完全な(深い)コピーを作成することを提案しますcopy.deepcopy()。これはやり過ぎかもしれませんが、copy.copy()またはを使用した「通常の」(浅い)コピーでdict.copy()十分かもしれません。辞書は、オブジェクトへの参照をキーの値として保持します。したがって、辞書からキーを削除すると、参照されているオブジェクトではなく、この参照が削除されます。メモリ内にオブジェクトへの参照が他にない場合、オブジェクト自体は後でガベージコレクタによって自動的に削除されます。ディープコピーを作成すると、シャローコピーに比べてより多くの計算が必要になるため、コピーを作成し、メモリを浪費し、GCにより多くの作業を提供することにより、コードのパフォーマンスが低下します。シャローコピーで十分な場合もあります。

ただし、ディクショナリ値として変更可能なオブジェクトがあり、返されたディクショナリでキーなしでそれらを後で変更する場合は、ディープコピーを作成する必要があります。

浅いコピー:

def get_dict_wo_key(dictionary, key):
    """Returns a **shallow** copy of the dictionary without a key."""
    _dict = dictionary.copy()
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

ディープコピー:

from copy import deepcopy


def get_dict_wo_key(dictionary, key):
    """Returns a **deep** copy of the dictionary without a key."""
    _dict = deepcopy(dictionary)
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

21

…アイテムを辞書から削除して、コピーを返す(つまり、元のアイテムを変更しない)にはどうすればよいですか?

A dictは、これに使用するのに不適切なデータ構造です。

確かに、dictをコピーしてコピーからポップすることは機能しますが、理解度のある新しいdictを構築することはできますが、すべてのコピーには時間がかかります。つまり、一定時間の操作を線形時間の操作に置き換えました。そして、生きているそれらのすべてのコピーは、スペース、つまりコピーあたりの線形スペースを使用します。

ハッシュアレイにマップされたトライなどの他のデータ構造は、まさにこの種のユースケース用に設計されています。要素を追加または削除すると、コピーが対数時間で返され、そのストレージのほとんどが元のと共有されます。1

もちろん、いくつかの欠点があります。パフォーマンスは一定ではなく対数的です(ただし、底が大きい場合、通常は32〜128です)。また、非変更APIをと同じにすることもできますがdict、「変更」APIは明らかに異なります。そして、何よりも、PythonにはHAMTバッテリーが含まれていません。2

pyrsistentライブラリは、Python用HAMTベースのdict-置換(および種々の他のタイプ)のかなり固体実装です。また、既存の変更コードを永続コードにできるだけスムーズに移植するための洗練されたAPIさえ備えています。ただし、変更ではなくコピーを返すことを明示的にしたい場合は、次のように使用します。

>>> from pyrsistent import m
>>> d1 = m(a=1, b=2)
>>> d2 = d1.set('c', 3)
>>> d3 = d1.remove('a')
>>> d1
pmap({'a': 1, 'b': 2})
>>> d2
pmap({'c': 3, 'a': 1, 'b': 2})
>>> d3
pmap({'b': 2})

それd3 = d1.remove('a')がまさに質問が求めていることです。

のような変更可能なデータ構造がdictあり、にlist埋め込まれているpmap場合でも、エイリアスの問題が発生します。これは、pmapsとpvectorsを埋め込んで不変に移動することによってのみ修正できます。


1. HAMTは、Scala、Clojure、Haskellなどの言語でも人気が高まっています。ロックフリープログラミングやソフトウェアトランザクションメモリで非常にうまく機能しますが、Pythonにはあまり関係がありません。

2.実際、stdlib に HAMTがあり、の実装で使用されていますcontextvars以前に撤回されたPEPはその理由を説明しています。ただし、これはライブラリの非表示の実装詳細であり、パブリックコレクションタイプではありません。



14

del d ['key']を呼び出すだけです。

ただし、本番環境では、dに「キー」が存在するかどうかを確認することを常にお勧めします。

if 'key' in d:
    del d['key']

7
うーん、いいえ、本番環境でEAFPイデオロギーに従うのが良いでしょう。try-exceptブロック内のキーを削除するだけです。少なくとも、これはアトミック操作になります;)
maxkoryukov

1
また、簡潔にしたい場合d.pop('key', None)は、を使用してください。しかし、実際の問題は、1つのキーなしで辞書を取得することであり、辞書を変更することではありませんでした。だから理解 -ここでは良い選択です;)
maxkoryukov '28年

7

いいえ、他に方法はありません

def dictMinus(dct, val):
   copy = dct.copy()
   del copy[val]
   return copy

ただし、わずかに変更されたディクショナリのコピーを作成することは、比較的大量のメモリを必要とするため、おそらくお勧めできません。通常、古い辞書をログに記録し(必要な場合でも)、変更することをお勧めします。


7
# mutate/remove with a default
ret_val = body.pop('key', 5)
# no mutation with a default
ret_val = body.get('key', 5)

5
>>> def delete_key(dict, key):
...     del dict[key]
...     return dict
... 
>>> test_dict = {'one': 1, 'two' : 2}
>>> print delete_key(test_dict, 'two')
{'one': 1}
>>>

これはエラー処理を行わず、キーが辞書にあることを前提としています。最初にそれを確認し、raiseそうでない場合は


10
どのようにあなたの方法はただ違うのdel test_dict[key]ですか?
Mad Physicist 2015

5

ここでトップレベルの設計アプローチ:

def eraseElement(d,k):
    if isinstance(d, dict):
        if k in d:
            d.pop(k)
            print(d)
        else:
            print("Cannot find matching key")
    else:
        print("Not able to delete")


exp = {'A':34, 'B':55, 'C':87}
eraseElement(exp, 'C')

ディクショナリと必要なキーを関数に渡し、それがディクショナリかどうかと、キーに問題がないかどうかを検証し、両方とも存在する場合は、ディクショナリから値を削除して、残りを出力します。

出力: {'B': 55, 'A': 34}

お役に立てば幸いです。


3

以下のコードスニペットは間違いなく役立ちます。コードを理解するのに役立つコメントを各行に追加しました。

def execute():
   dic = {'a':1,'b':2}
   dic2 = remove_key_from_dict(dic, 'b')  
   print(dict2)           # {'a': 1}
   print(dict)            # {'a':1,'b':2}

def remove_key_from_dict(dictionary_to_use, key_to_delete):
   copy_of_dict = dict(dictionary_to_use)     # creating clone/copy of the dictionary
   if key_to_delete in copy_of_dict :         # checking given key is present in the dictionary
       del copy_of_dict [key_to_delete]       # deleting the key from the dictionary 
   return copy_of_dict                        # returning the final dictionary

または、dict.pop()を使用することもできます

d = {"a": 1, "b": 2}

res = d.pop("c")  # No `KeyError` here
print (res)       # this line will not execute

またはより良いアプローチは

res = d.pop("c", "key not found")
print (res)   # key not found
print (d)     # {"a": 1, "b": 2}

res = d.pop("b", "key not found")
print (res)   # 2
print (d)     # {"a": 1}

2

リスト内包表記を使用した別のバリエーションを次に示します。

original_d = {'a': None, 'b': 'Some'}
d = dict((k,v) for k, v in original_d.iteritems() if v)
# result should be {'b': 'Some'}

このアプローチは、この投稿からの回答に基づいています: 辞書から空の文字列を持つキーを削除する効率的な方法


1
すでにシンプルで適切な、受け入れられた答えがすでにある長年の質問に答えるつもりなら、少なくともあなたの答えが正しいことを確認してください。これは、OPが要求したことを行いません。
user2357112は2013

貴重な情報が追加される可能性があると思う質問の日付は通常チェックしません。また、私はにリンクされている質問のコメントの1あたりに:「通常、これは、誰かが望んでいるまさにあり、おそらくどのようなOPが必要であるが、それはOPがを求めたものではない」stackoverflow.com/questions/12118695/...の I質問に対する直接的な回答ではないことを知っていました。むしろオプションの拡張です。
BigBlueHat 2013

3
この回答は、完全ではありませんが、if条件でもアイテムを削除できることを示しています。op if vif k is not 'a'答えるように変更するだけです。しかし、これは効率的な方法ではないと思います。これにより、popまたはdelのようにO(log n)ではなくO(n)の要素が削除されます。
holgac 2014年

0
    species = {'HI': {'1': (1215.671, 0.41600000000000004),
  '10': (919.351, 0.0012),
  '1025': (1025.722, 0.0791),
  '11': (918.129, 0.0009199999999999999),
  '12': (917.181, 0.000723),
  '1215': (1215.671, 0.41600000000000004),
  '13': (916.429, 0.0005769999999999999),
  '14': (915.824, 0.000468),
  '15': (915.329, 0.00038500000000000003),
 'CII': {'1036': (1036.3367, 0.11900000000000001), '1334': (1334.532, 0.129)}}

次のコードは、dictのコピーを作成し、species存在しないアイテムを削除しますtrans_HI

trans_HI=['1025','1215']
for transition in species['HI'].copy().keys():
    if transition not in trans_HI:
        species['HI'].pop(transition)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.