2つのPython辞書を1つの式にマージするにはどうすればよいですか?
辞書用x
とy
、z
浅くはからの値で辞書合併となりy
からのものに置き換えますx
。
Python 3.5以降の場合:
z = {**x, **y}
Python 2(または3.4以下)では、関数を記述します。
def merge_two_dicts(x, y):
z = x.copy() # start with x's keys and values
z.update(y) # modifies z with y's keys and values & returns None
return z
そしていま:
z = merge_two_dicts(x, y)
Pythonの3.9.0a4では以上の(最終的なリリース日約2020年10月):PEP-584、ここで議論し、さらにこれを簡素化するために実装されました:
z = x | y # NOTE: 3.9+ ONLY
説明
あなたが2つのdictsを持っていて、元のdictsを変更せずにそれらを新しいdictにマージしたいとします:
x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
望ましい結果はz
、値がマージされた新しい辞書()を取得し、2番目の辞書の値が最初の辞書の値を上書きすることです。
>>> z
{'a': 1, 'b': 3, 'c': 4}
このために新しい構文に提案PEP 448とのPython 3.5のように利用可能であり、
z = {**x, **y}
そして、それは確かに単一の表現です。
リテラル表記でもマージできることに注意してください。
z = {**x, 'foo': 1, 'bar': 2, **y}
そしていま:
>>> z
{'a': 1, 'b': 3, 'foo': 1, 'bar': 2, 'c': 4}
今で実装として表示される3.5、PEP 478のリリーススケジュール、およびそれが今にその方法をしたのPython 3.5での新機能ドキュメント。
ただし、多くの組織はまだPython 2を使用しているため、下位互換性のある方法でこれを行うことができます。Python 2およびPython 3.0-3.4で利用できる古典的なPythonの方法は、これを2段階のプロセスとして実行することです。
z = x.copy()
z.update(y) # which returns None since it mutates z
両方のアプローチでは、y
二来るとその値が置き換えられますx
ので、の値を'b'
指します3
私たちの最終的な結果に。
まだPython 3.5ではありませんが、単一の式が必要です
まだPython 3.5を使用していない場合、または下位互換性のあるコードを記述する必要があり、これを単一の式で作成したい場合、正しい方法で最もパフォーマンスが高いのは、関数にコードを配置することです。
def merge_two_dicts(x, y):
"""Given two dicts, merge them into a new dict as a shallow copy."""
z = x.copy()
z.update(y)
return z
そして、あなたは単一の式を持っています:
z = merge_two_dicts(x, y)
ゼロから非常に大きな数まで、未定義の数の辞書をマージする関数を作成することもできます。
def merge_dicts(*dict_args):
"""
Given any number of dicts, shallow copy and merge into a new dict,
precedence goes to key value pairs in latter dicts.
"""
result = {}
for dictionary in dict_args:
result.update(dictionary)
return result
この関数は、Python 2および3ですべての辞書に対して機能します。たとえば、次のように口述a
を与えられますg
:
z = merge_dicts(a, b, c, d, e, f, g)
内のキーと値のペアはg
、dict a
からへの優先順位よりも優先されますf
。
他の回答の批評
以前に受け入れられた回答にあるものを使用しないでください。
z = dict(x.items() + y.items())
Python 2では、各辞書のメモリに2つのリストを作成し、最初の2つをまとめた長さと同じ長さの3つ目のリストをメモリに作成し、3つのリストすべてを破棄して辞書を作成します。Python 3では、dict_items
2つのリストではなく2 つのオブジェクトを一緒に追加しているため、これは失敗します-
>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'
そして、それらをリストとして明示的に作成する必要がありますz = dict(list(x.items()) + list(y.items()))
。これは、リソースと計算能力の浪費です。
同様に、items()
Python 3(viewitems()
Python 2.7)のの結合は、値がハッシュできないオブジェクト(リストなど)の場合も失敗します。値がハッシュ可能であっても、セットは意味的に順序付けられていないため、優先順位に関して動作は定義されていません。したがって、これを行わないでください。
>>> c = dict(a.items() | b.items())
この例は、値がハッシュ可能でない場合にどうなるかを示しています。
>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
yを優先する必要がある例を次に示しますが、代わりに、セットの任意の順序により、xからの値が保持されます。
>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}
使用してはいけない別のハック:
z = dict(x, **y)
これはdict
コンストラクターを使用し、非常に高速でメモリ効率が良いです(2ステップのプロセスよりも少し多いです)が、ここで何が起こっているか正確にわかっていない場合(つまり、2番目のdictがキーワード引数としてdictに渡されています)コンストラクタ)、読みにくく、意図した使用法ではないため、Pythonicではありません。
djangoで修正された使用法の例を次に示します。
Dictはハッシュ可能なキー(例:frozensetsまたはtuples)を取ることを目的としていますが、キーが文字列でない場合、このメソッドはPython 3で失敗します。
>>> c = dict(a, **b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings
メーリングリストから、言語の作成者であるグイドファンロッサムは次のように書いています。
私はdict({}、** {1:3})を違法と宣言することに問題はありません。結局それは**メカニズムの乱用だからです。
そして
どうやらdict(x、** y)は「call x.update(y)and return x」の「クールなハック」として機能しています。個人的にはかっこいいというよりは卑劣だと思います。
私が理解していること(および言語の作成者を理解していること)は、の意図された使用法dict(**y)
が可読性の目的で辞書を作成するためのものであることを示しています。
dict(a=1, b=10, c=11)
の代わりに
{'a': 1, 'b': 10, 'c': 11}
コメントへの対応
グイドが言ったことにもかかわらずdict(x, **y)
、それはdict仕様に沿っています。Python 2と3の両方で機能します。これが文字列キーでのみ機能するという事実は、キーワードパラメータの機能の直接的な結果であり、dictの短命ではありません。この場所で**演算子を使用することも、メカニズムの乱用であることもありません。実際、**は、dictsをキーワードとして渡すように正確に設計されています。
繰り返しになりますが、キーが文字列以外の場合、3では機能しません。暗黙の呼び出し規約では、名前空間は通常のディクテーションを使用しますが、ユーザーは文字列であるキーワード引数のみを渡す必要があります。他のすべての呼び出し可能オブジェクトがそれを強制しました。dict
Python 2でこの一貫性を壊しました:
>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}
Pythonの他の実装(Pypy、Jython、IronPython)を考えると、この矛盾は悪かった。したがって、この使用法は重大な変更になる可能性があるため、Python 3で修正されました。
言語の1つのバージョンでのみ機能する、または特定の任意の制約が与えられた場合にのみ機能するコードを意図的に作成することは、悪意のある能力がないことを私はあなたに提出します。
その他のコメント:
dict(x.items() + y.items())
まだPython 2の最も読みやすいソリューションです。読みやすさが重要です。
私の応答:merge_two_dicts(x, y)
実際に読みやすさを気にしているのであれば、実際には私にははるかに明確に思えます。また、Python 2のサポートは終了しているため、上位互換性はありません。
{**x, **y}
ネストされた辞書を処理していないようです。ネストされたキーの内容はマージされずに単に上書きされます[...]再帰的にマージしないこれらの回答に火傷を負ってしまい、誰もそれに言及していなかったことに驚きました。「マージ」という言葉の私の解釈では、これらの答えは「1つの辞書を別の辞書で更新する」ことであり、マージではないことを表しています。
はい。1 つの式で、最初の値が2番目の値で上書きされるという2 つの辞書の浅いマージを求める質問に戻って参照する必要があります。
辞書の2つの辞書を想定すると、1つの関数でそれらを再帰的にマージする可能性がありますが、どちらのソースからも辞書を変更しないように注意する必要があります。これを回避する最も確実な方法は、値を割り当てるときにコピーを作成することです。キーはハッシュ可能でなければならず、通常は不変であるため、キーをコピーしても意味がありません。
from copy import deepcopy
def dict_of_dicts_merge(x, y):
z = {}
overlapping_keys = x.keys() & y.keys()
for key in overlapping_keys:
z[key] = dict_of_dicts_merge(x[key], y[key])
for key in x.keys() - overlapping_keys:
z[key] = deepcopy(x[key])
for key in y.keys() - overlapping_keys:
z[key] = deepcopy(y[key])
return z
使用法:
>>> x = {'a':{1:{}}, 'b': {2:{}}}
>>> y = {'b':{10:{}}, 'c': {11:{}}}
>>> dict_of_dicts_merge(x, y)
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}
他の値タイプの不測の事態を考えることはこの質問の範囲をはるかに超えているので、「辞書の辞書のマージ」に関する標準的な質問に対する私の回答を紹介します。
パフォーマンスは低いが適切なアドホック
これらのアプローチはパフォーマンスが低くなりますが、正しい動作を提供します。彼らは、あろうはるかに少ないパフォーマンス以上copy
とupdate
新しいアンパックそれらは抽象度の高い各キーと値のペアを反復するので、または、それらは実行優先順位を(後者dictsが優先を有する)尊重します
dict内包内でdictを手動でチェーンすることもできます。
{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7
またはpython 2.6(およびおそらくジェネレータ式が導入されたときの2.4まで):
dict((k, v) for d in dicts for k, v in d.items())
itertools.chain
正しい順序でキーと値のペアにイテレータをチェーンします:
import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))
パフォーマンス分析
正しく動作することがわかっている使用法のパフォーマンス分析のみを実行します。
import timeit
以下はUbuntu 14.04で行われます
Python 2.7(システムPython)の場合:
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934
Python 3.5(デッドスネークPPA):
>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287
辞書に関するリソース
z = x | y