Pythonで1つの式に2つの辞書をマージするにはどうすればよいですか?


4787

2つのPython辞書があり、これらの2つの辞書をマージして返す単一の式を書きたいのですが。update()その場所でディクショナリを変更する代わりにその結果を返すのであれば、このメソッドが必要です。

>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> z = x.update(y)
>>> print(z)
None
>>> x
{'a': 1, 'b': 10, 'c': 11}

どのように私はその中で、最終的なマージされた辞書を得ることができz、ありませんかx

(さらに明確にするために、最後に勝った競合処理dict.update()は私が探しているものでもあります。)


1
Python 3.9 alphaを使用している場合は、次のように使用してくださいz = x | y
The Daleks

回答:


5704

2つのPython辞書を1つの式にマージするにはどうすればよいですか?

辞書用xyz浅くはからの値で辞書合併となり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では機能しません。暗黙の呼び出し規約では、名前空間は通常のディクテーションを使用しますが、ユーザーは文字列であるキーワード引数のみを渡す必要があります。他のすべての呼び出し可能オブジェクトがそれを強制しました。dictPython 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: {}}}

他の値タイプの不測の事態を考えることはこの質問の範囲をはるかに超えているので、「辞書の辞書のマージ」に関する標準的な質問に対する私の回答を紹介します。

パフォーマンスは低いが適切なアドホック

これらのアプローチはパフォーマンスが低くなりますが、正しい動作を提供します。彼らは、あろうはるかに少ないパフォーマンス以上copyupdate新しいアンパックそれらは抽象度の高い各キーと値のペアを反復するので、または、それらは実行優先順位を(後者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

辞書に関するリソース


9
@MohammadAzimの「文字列のみ」は、一般化されたアンパッキング構文ではなく、呼び出し可能オブジェクトのキーワード引数の展開にのみ適用されます。これが機能することを示すには:{**{(0, 1):2}}->{(0, 1): 2}
アーロンホール

37
z = {**x, **y}本当に私を刺激するような短い答え
pcko1

1
これは、PEP-0584が受け入れられたときに変更される場合があります。新しいunion演算子は、次の構文を使用して実装されます:x | y
Callamデラニー

2
回答の上部に要約が必要な場合、それは長すぎます。
グリンゴSuave

2
こんにちは、トップは要約です、はい。あなた次第。すべてが素晴らしいブログ投稿になります。注Py 3.4以下はEOL、3.5は2020-09でEOLに近づいています。
グリンゴSuave

1618

あなたの場合、あなたができることは:

z = dict(x.items() + y.items())

これは、あなたが望むように、最後の辞書をに入れ、zkeyの値bが2番目の(y)dictの値によって適切に上書きされるようにします:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(x.items() + y.items())
>>> z
{'a': 1, 'c': 11, 'b': 10}

Python 3を使用する場合は、少しだけ複雑です。作成するにはz

>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}

Pythonバージョン3.9.0a4以降を使用している場合は、次のものを直接使用できます。

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = x | y
print(z)

Output: {'a': 1, 'c': 11, 'b': 10}

3
これは非常に非効率なので、使用しないでください。(以下のtimeitの結果を参照してください。)ラッパー関数がオプションでなかった場合、Py2日に必要であったかもしれませんが、それらの日は現在過ぎています。
グリンゴSuave

632

代替:

z = x.copy()
z.update(y)

83
これが質問によって提供された基準を満たさない理由を明確にするために:それは単一の式ではなく、zを返しません。
Alex

2
@neuronetすべてのonelinerは通常、異なるコンポーネントで発生するコードを移動し、そこで解決します。これは間違いなくケースの1つです。ただし、他の言語には、Pythonよりも優れた構成があります。そして、その要素を返す参照透過的なバリアントを持つことは良いことです。
Alex

12
このように記述します。コードを渡した相手に1行のコードを説明する2行のコメントを追加する必要がある場合...本当に1行で実行しましたか?:)私はPythonがこれに適していないことに完全に同意します。この答えはよりpythonicですが、それは本当にすべて明示的または明確ですか?Updateは、ユーザーがよく使用する「コア」機能の1つではありません。
エリック

まあ、人々がそれをワンライナーにすることを主張するならば、あなたはいつでもすることができます(lambda z: z.update(y) or z)(x.copy()):P
towr

341

別の、より簡潔なオプション:

z = dict(x, **y)

:これは人気のある答えになっていyますが、に文字列以外のキーがある場合、これがまったく機能するという事実はCPython実装の詳細の乱用であり、Python 3では機能しないことを指摘することが重要ですまたはPyPy、IronPython、またはJython。また、Guidoはファンではありません。したがって、この手法を前方互換またはクロス実装のポータブルコードに推奨することはできません。つまり、完全に回避する必要があります。


Python 3とPyPyおよびPyPy 3正常に動作し、 JythonまたはIronと通信できません。このパターンが明示的に文書化されている場合(この文書の3番目のコンストラクターフォームを参照)、これは「実装の詳細」ではなく、意図的な機能の使用であると主張します。
amcgregor

5
@amcgregor「yに文字列以外のキーがある場合」というキーフレーズを逃しました。これはPython3では機能しません。CPython 2で機能するという事実は、信頼できない実装の詳細です。IFFすべてのキーは文字列であることが保証されています。これは完全にサポートされているオプションです。
カールマイヤー

214

これはおそらく一般的な答えにはなりませんが、ほとんどの場合、これを実行する必要はありません。マージしたコピーが必要な場合は、copy(または、必要に応じてdeepcopy)を使用してから更新します。2行のコードは、.items()+ .items()を使用した1行の作成よりもはるかに読みやすく(Pythonicに近く)なります。明示的は暗黙的よりも優れています。

さらに、.items()(Python 3.0以前)を使用すると、dictからのアイテムを含む新しいリストが作成されます。辞書が大きい場合、それはかなりのオーバーヘッドになります(マージされた辞書が作成されるとすぐに破棄される2つの大きなリスト)。update()は、2番目のdictを項目ごとに実行できるため、より効率的に機能します。

時間の面で:

>>> timeit.Timer("dict(x, **y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.52571702003479
>>> timeit.Timer("temp = x.copy()\ntemp.update(y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.694622993469238
>>> timeit.Timer("dict(x.items() + y.items())", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
41.484580039978027

IMO最初の2つの間のわずかな速度低下は、読みやすさの点で価値があります。さらに、辞書作成のキーワード引数はPython 2.3でのみ追加されましたが、copy()とupdate()は古いバージョンで機能します。


150

フォローアップの回答で、次の2つの選択肢の相対的なパフォーマンスについて質問しました。

z1 = dict(x.items() + y.items())
z2 = dict(x, **y)

私のマシンでは、少なくとも(Python 2.5.2を実行しているごく普通のx86_64)、代替z2は短くて単純であるだけでなく、大幅に高速です。timeitPythonに付属のモジュールを使用して、これを自分で確認できます。

例1:20個の連続した整数をそれ自体にマッピングする同一の辞書:

% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z1=dict(x.items() + y.items())'
100000 loops, best of 3: 5.67 usec per loop
% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z2=dict(x, **y)' 
100000 loops, best of 3: 1.53 usec per loop

z23.5倍程度勝ちます。辞書が異なれば結果もまったく違うように見えますが、z2常に先に出てくるようです。(同じテストで一貫性のない結果が得られた場合-rは、デフォルトの3より大きい数値を渡してみてください。)

例2:252の短い文字列を整数に、またはその逆にマッピングする重複しない辞書:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z1=dict(x.items() + y.items())'
1000 loops, best of 3: 260 usec per loop
% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z2=dict(x, **y)'               
10000 loops, best of 3: 26.9 usec per loop

z2 約10倍勝ちます。これは、私の本ではかなり大きな勝利です。

これら2つを比較した後、私はのz1パフォーマンスの低下が2つの項目リストを作成するオーバーヘッドに起因しているのではないかと疑問に思いました。

from itertools import chain
z3 = dict(chain(x.iteritems(), y.iteritems()))

いくつかの簡単なテスト、例えば

% python -m timeit -s 'from itertools import chain; from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z3=dict(chain(x.iteritems(), y.iteritems()))'
10000 loops, best of 3: 66 usec per loop

それz3よりも少し速いと結論づけますがz1、ほど速くはありませんz2。間違いなくすべての追加のタイピングの価値はありません。

この議論には、まだ重要なものが欠けています。これは、これらの代替案と、2つのリストをマージする「明白な」方法、つまりupdateメソッドの使用とのパフォーマンス比較です。式を使って物事を平等に保つことを試みるために、いずれもxまたはyを変更しないため、次のようにxをインプレースで変更する代わりに、xのコピーを作成します。

z0 = dict(x)
z0.update(y)

典型的な結果:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z0=dict(x); z0.update(y)'
10000 loops, best of 3: 26.9 usec per loop

言い換えれば、z0z2本質的に同一の性能を持っているようです。これは偶然かもしれないと思いますか?私はしません...

実際、私は、純粋なPythonコードではこれ以上のことを行うことは不可能だと主張するところまで行きます。また、C拡張モジュールで大幅に改善できる場合は、Pythonの人々がコード(またはアプローチのバリエーション)をPythonコアに組み込むことに関心を持っていると思います。Pythonはdict多くの場所で使用しています。その運用を最適化することは大きな問題です。

次のように書くこともできます

z0 = x.copy()
z0.update(y)

Tonyと同様ですが、(驚くことではありませんが)表記の違いは、パフォーマンスに測定可能な影響を与えないことがわかります。自分に合っている方を使用してください。もちろん、彼は2文バージョンの方がはるかに理解しやすいことを指摘するのは間違いなく正しいです。


5
これはPython 3では機能しません。items()受け入れ可能でiteritemsはなく、存在しません。
Antti Haapala 2015年

127

Python 3.0以降ではcollections.ChainMap複数のdictまたは他のマッピングをグループ化して、単一の更新可能なビューを作成できます。

>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(ChainMap({}, y, x))
>>> for k, v in z.items():
        print(k, '-->', v)

a --> 1
b --> 10
c --> 11

Python 3.5以降の更新PEP 448拡張ディクショナリのパックおよびアンパックを使用できます。これは速くて簡単です:

>>> x = {'a':1, 'b': 2}
>>> y = y = {'b':10, 'c': 11}
>>> {**x, **y}
{'a': 1, 'b': 10, 'c': 11}

3
ただし、ChainMapの使用中は注意が必要です。重複するキーがある場合、最初のマッピングの値が使用され、delon を呼び出すと、ChainMap cがそのキーの最初のマッピングを削除するという問題があります。
スレイヤー

7
@Prerit他に何を期待しますか?これが、チェーンされた名前空間が機能する通常の方法です。$ PATHがbashでどのように機能するかを検討します。パス上の実行可能ファイルを削除しても、上流にある同じ名前の別の実行可能ファイルが除外されることはありません。
レイモンドヘッティンガー2017

2
@Raymond Hettinger同意する、ただ警告を追加した。ほとんどの人はそれについて知らないかもしれません。:D
スレイヤー

@Preritこれdictを回避するためにキャストできます。つまり、dict(ChainMap({}, y, x))
wjandrea

113

私は似たようなものを望みましたが、重複するキーの値がどのようにマージされるかを指定する機能を備えていたので、これをハッキングしました(ただし、厳密にはテストしませんでした)。明らかにこれは単一の式ではなく、単一の関数呼び出しです。

def merge(d1, d2, merge_fn=lambda x,y:y):
    """
    Merges two dictionaries, non-destructively, combining 
    values on duplicate keys as defined by the optional merge
    function.  The default behavior replaces the values in d1
    with corresponding values in d2.  (There is no other generally
    applicable merge strategy, but often you'll have homogeneous 
    types in your dicts, so specifying a merge technique can be 
    valuable.)

    Examples:

    >>> d1
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1)
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1, lambda x,y: x+y)
    {'a': 2, 'c': 6, 'b': 4}

    """
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge_fn(result[k], v)
        else:
            result[k] = v
    return result

88

再帰的/ディープで辞書を更新する

def deepupdate(original, update):
    """
    Recursively update a dict.
    Subdict's won't be overwritten but also updated.
    """
    for key, value in original.iteritems(): 
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            deepupdate(value, update[key]) 
    return update

デモンストレーション:

pluto_original = {
    'name': 'Pluto',
    'details': {
        'tail': True,
        'color': 'orange'
    }
}

pluto_update = {
    'name': 'Pluutoo',
    'details': {
        'color': 'blue'
    }
}

print deepupdate(pluto_original, pluto_update)

出力:

{
    'name': 'Pluutoo',
    'details': {
        'color': 'blue',
        'tail': True
    }
}

編集ありがとうございました。


1
これは質問の答えにはなりません。質問は明らかに、元の辞書xとyからの新しい辞書zを要求します。更新された辞書ではなく、yの値がxの値を置き換えます。この答えは、xから値を追加することにより、yをインプレースで変更します。さらに悪いことに、これらの値はコピーされないため、変更されたディクショナリyをさらに変更し、変更をディクショナリxに反映できます。@Jérômeこのコードがアプリケーションにバグを引き起こさないことを願っています。少なくとも、deepcopyを使用して値をコピーすることを検討してください。
アーロンホール

1
@AaronHallはこれが質問に答えないことに同意しました。しかし、それは私のニーズに答えます。私はそれらの制限を理解していますが、私の場合は問題ではありません。考えてみれば、ディープコピーを呼び起こす可能性があるため、名前が誤解を招く可能性があります。しかし、それは深い入れ子を扱います。次に、Martellibotの別の実装を示します。stackoverflow.com/ questions/ 3232943/
ジェローム2018年

72

コピーを使用しないときに考えられる最高のバージョンは次のとおりです。

from itertools import chain
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
dict(chain(x.iteritems(), y.iteritems()))

少なくともCPythonでは、それよりも高速ですdict(x.items() + y.items())が、ほど高速ではありませんn = copy(a); n.update(b)。このバージョンは、2to3ツールによって自動的に実行されるに変更iteritems()した場合、Python 3でも機能しitems()ます。

個人的には、このバージョンが一番好きです。これは、単一の関数構文で必要なものをかなりよく説明しているためです。唯一のマイナーな問題は、yの値がxの値よりも優先されることを完全には明らかにしないことですが、それを理解するのは難しいとは思いません。


71

Python 3.5(PEP 448)では、より優れた構文オプションを使用できます。

x = {'a': 1, 'b': 1}
y = {'a': 2, 'c': 2}
final = {**x, **y} 
final
# {'a': 2, 'b': 1, 'c': 2}

あるいは

final = {'a': 1, 'b': 1, **x, **y}

Python 3.9では、|も使用します。および| = PEP 584の以下の例

d = {'spam': 1, 'eggs': 2, 'cheese': 3}
e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
d | e
# {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}

このソリューションは-ソリューションよりどのように優れていdict(x, **y)ますか?あなた(@CarlMeyer)があなた自身の回答のメモ(stackoverflow.com/a/39858/2798610)内で言及したように、Guidoはそのソリューションを違法と見なしています。
Blackeagle52 2015年

14
Guidoはdict(x, **y)(非常に良い)理由で、y有効なキーワード引数名​​であるキーのみに依存しているのが嫌いです(dictコンストラクターが不正を行うCPython 2.7を使用している場合を除く)。この異議/制限はPEP 448には適用されません**。PEP448 は、アンパック構文を一般化してリテラルを指示します。そのため、このソリューションにはと同じようdict(x, **y)に、欠点はありません。
Carl Meyer

62
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

両方の辞書にキーがあるアイテム( 'b')の場合、最後に置くことで、どちらが最終的に出力されるかを制御できます。


Python 3ではTypeError:unsupported operand type(s)for +: 'dict_items' and 'dict_items' ...を取得します。次のように、各dictをlist()でカプセル化する必要があります:dict(list(x.items())+ list (y.items()))
justSaid

49

質問はすでに何度か回答されていますが、問題に対するこの簡単な解決策はまだリストされていません。

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z4 = {}
z4.update(x)
z4.update(y)

上記のz0やevil z2と同じくらい高速ですが、理解と変更が簡単です。


3
しかし、これは1つの式ではなく3つのステートメントです
Fortran

14
はい!前述の1つの式による解決策は、時間がかかるか悪です。優れたコードは読みやすく、保守が容易です。だから問題は答えではなく質問です。1行の解決策ではなく、問題の最善の解決策を求める必要があります。
phobie

7
を失ってz4 = {}、次の行を変更しますz4 = x.copy()-良いコードが不必要なことをしない(それにより、コードがより読みやすく、保守しやすくなります)。
martineau 2013年

3
あなたの提案はこれをマシューズの答えに変えるでしょう。彼の答えはすばらしいですが、私の方が読みやすく、保守しやすいと思います。余分な行は、実行時間がかかる場合にのみ問題になります。
phobie 2013年

47
def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)

そのような怪しげで疑わしい答えの中で、この輝かしい例はPythonで口述をマージするための唯一無二の良い方法であり、Guido van Rossum自身の独裁者によって承認されています!他の誰かがこれの半分を提案しましたが、関数には入れませんでした。

print dict_merge(
      {'color':'red', 'model':'Mini'},
      {'model':'Ferrari', 'owner':'Carl'})

与える:

{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}

39

ラムダは悪だと思うなら、これ以上読んではいけません。要求に応じて、1つの式で高速かつメモリ効率の高いソリューションを記述できます。

x = {'a':1, 'b':2}
y = {'b':10, 'c':11}
z = (lambda a, b: (lambda a_copy: a_copy.update(b) or a_copy)(a.copy()))(x, y)
print z
{'a': 1, 'c': 11, 'b': 10}
print x
{'a': 1, 'b': 2}

上で提案したように、2行を使用するか、関数を書くことはおそらくより良い方法です。


33

pythonicになる。理解度を使用する:

z={i:d[i] for d in [x,y] for i in d}

>>> print z
{'a': 1, 'c': 11, 'b': 10}

1
関数として:def dictmerge(*args): return {i:d[i] for d in args for i in d}
jessexknight

1
直接のキー/値のペアを反復することにより、ルックアップ割引:z={k: v for d in (x, y) for k, v in d.items()}
ShadowRanger

30

python3では、itemsメソッドはリストを返すのではなく、セットのように機能するビューを返します。この場合、との連結+は機能しないため、セットユニオンを使用する必要があります。

dict(x.items() | y.items())

バージョン2.7でのpython3のような動作の場合、このviewitemsメソッドは次の代わりに機能しitemsます。

dict(x.viewitems() | y.viewitems())

タイトルが示すように、この表記は連結ではなく集合和演算と考える方が自然なため、とにかくこの表記を好みます。

編集:

python 3のポイントをもう2つ。最初にdict(x, **y)、キーyが文字列でない限り、python 3 ではトリックが機能しないことに注意してください。

また、レイモンドヘッティンガーのチェーンマップの回答は、引数として任意の数のディクテーションを受け取ることができるため、非常にエレガントですが、ドキュメントからは、ルックアップごとにすべてのディクテーションのリストを順番に調べているように見えます。

ルックアップは、キーが見つかるまで、基礎となるマッピングを順次検索します。

アプリケーションでルックアップが多い場合、これにより速度が低下する可能性があります。

In [1]: from collections import ChainMap
In [2]: from string import ascii_uppercase as up, ascii_lowercase as lo; x = dict(zip(lo, up)); y = dict(zip(up, lo))
In [3]: chainmap_dict = ChainMap(y, x)
In [4]: union_dict = dict(x.items() | y.items())
In [5]: timeit for k in union_dict: union_dict[k]
100000 loops, best of 3: 2.15 µs per loop
In [6]: timeit for k in chainmap_dict: chainmap_dict[k]
10000 loops, best of 3: 27.1 µs per loop

したがって、検索では1桁遅くなります。私はChainmapのファンですが、ルックアップが多い場合は実用的ではありません。


22

マシューの答え一式で解く虐待:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (lambda f=x.copy(): (f.update(y), f)[1])()
>>> z
{'a': 1, 'c': 11, 'b': 10}

1つの式が必要だと言ったので、私lambdaは名前をバインドするために悪用し、タプルはラムダの1つの式の制限をオーバーライドしました。緊張してください。

もちろん、コピーする必要がない場合は、これを行うこともできます。

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (x.update(y), x)[1]
>>> z
{'a': 1, 'b': 10, 'c': 11}

22

順序を保持するitertoolsを使用した簡単なソリューション(後の方が優先されます)

import itertools as it
merge = lambda *args: dict(it.chain.from_iterable(it.imap(dict.iteritems, args)))

そしてそれは使用法です:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> merge(x, y)
{'a': 1, 'b': 10, 'c': 11}

>>> z = {'c': 3, 'd': 4}
>>> merge(x, y, z)
{'a': 1, 'b': 10, 'c': 3, 'd': 4}


16

回答がこの浅い辞書に適していたとしても、ここで定義されているメソッドのいずれも、実際には深い辞書のマージを行いません。

次に例を示します。

a = { 'one': { 'depth_2': True }, 'two': True }
b = { 'one': { 'extra': False } }
print dict(a.items() + b.items())

次のような結果が期待されます。

{ 'one': { 'extra': False', 'depth_2': True }, 'two': True }

代わりに、これを取得します。

{'two': True, 'one': {'extra': False}}

'one'エントリは、本当にマージである場合、ディクショナリ内のアイテムとして 'depth_2'および 'extra'を持っている必要があります。

チェーンを使用しても機能しません:

from itertools import chain
print dict(chain(a.iteritems(), b.iteritems()))

結果:

{'two': True, 'one': {'extra': False}}

rcwesickが行ったディープマージでも同じ結果が得られます。

はい、サンプル辞書をマージすることはできますが、マージするための一般的なメカニズムはありません。本当のマージを行うメソッドを書いたら、後でこれを更新します。


11

(Python2.7 *のみ。Python3*のより簡単なソリューションがあります。)

標準ライブラリモジュールのインポートを嫌わない場合は、

from functools import reduce

def merge_dicts(*dicts):
    return reduce(lambda a, d: a.update(d) or a, dicts, {})

(常に成功すると戻るためor a、のビットlambdaが必要です。)dict.updateNone


11

変更しても構わない場合はx

x.update(y) or x

シンプルで読みやすく、高性能。あなた update()常にNonefalseを返すを知っています。したがって、上記の式はx更新後、常にに評価されます。

標準ライブラリの(のような.update())変異メソッドはNone慣例により戻るので、このパターンはそれらでも機能します。この規則に従わない方法を使用している場合、機能しないor可能性があります。ただし、タプル表示とインデックスを使用して、単一の式にすることができます。これは、最初の要素が何に評価されるかに関係なく機能します。

(x.update(y), x)[-1]

x変数をまだ持っていない場合は、を使用lambdaして、割り当てステートメントを使用せずにローカルを作成できます。これは、関数型言語では一般的な手法でlambdaあるlet式として使用することになりますが、Pythonを使用しない場合もあります。

(lambda x: x.update(y) or x)({'a': 1, 'b': 2})

次の新しいセイウチ演算子(Python 3.8+のみ)の使用とそれほど変わりませんが、

(x := {'a': 1, 'b': 2}).update(y) or x

コピーが必要な場合は、PEP 448スタイルが最も簡単{**x, **y}です。しかし、それが(古い)Pythonバージョンで利用できない場合、letパターンはここでも機能します。

(lambda z: z.update(y) or z)(x.copy())

(もちろん、これはと同等(z := x.copy()).update(y) or zですが、Pythonのバージョンがそれに対して十分新しい場合は、PEP 448スタイルを使用できます。)


10

ここや他の場所でアイデアを引き出して、関数を理解しました:

def merge(*dicts, **kv): 
      return { k:v for d in list(dicts) + [kv] for k,v in d.items() }

使用法(Python 3でテスト済み):

assert (merge({1:11,'a':'aaa'},{1:99, 'b':'bbb'},foo='bar')==\
    {1: 99, 'foo': 'bar', 'b': 'bbb', 'a': 'aaa'})

assert (merge(foo='bar')=={'foo': 'bar'})

assert (merge({1:11},{1:99},foo='bar',baz='quux')==\
    {1: 99, 'foo': 'bar', 'baz':'quux'})

assert (merge({1:11},{1:99})=={1: 99})

代わりにラムダを使用できます。


10

これまでに挙げた解決策の問題は、マージされた辞書ではキー "b"の値は10ですが、私の考えでは、それは12である必要があるということです。

import timeit

n=100000
su = """
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
"""

def timeMerge(f,su,niter):
    print "{:4f} sec for: {:30s}".format(timeit.Timer(f,setup=su).timeit(n),f)

timeMerge("dict(x, **y)",su,n)
timeMerge("x.update(y)",su,n)
timeMerge("dict(x.items() + y.items())",su,n)
timeMerge("for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k] ",su,n)

#confirm for loop adds b entries together
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]
print "confirm b elements are added:",x

結果:

0.049465 sec for: dict(x, **y)
0.033729 sec for: x.update(y)                   
0.150380 sec for: dict(x.items() + y.items())   
0.083120 sec for: for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]

confirm b elements are added: {'a': 1, 'c': 11, 'b': 12}

1
あなたは興味があるかもしれませんcytoolz.merge_withtoolz.readthedocs.io/en/latest/...
BLI

10

.update何も返さないのはばかげている。
私は単純なヘルパー関数を使用して問題を解決します:

def merge(dict1,*dicts):
    for dict2 in dicts:
        dict1.update(dict2)
    return dict1

例:

merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy

10
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

これで問題が解決するはずです。


9

これは、単一の辞書の理解で行うことができます:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> { key: y[key] if key in y else x[key]
      for key in set(x) + set(y)
    }

私の見解では、追加の関数は必要なく、短いので、「単一の式」の部分に対する最良の答えです。


とはいえ、パフォーマンスはあまり良くないと思います。各辞書からセットを作成し、キーを反復するだけで、毎回値をもう一度検索します(比較的高速ですが、スケーリングの関数の次数が増加します)
Breezer

2
それはすべて、使用しているpythonのバージョンに依存します。3.5以降では、{** x、** y}は連結された辞書を提供します
Rashid Mv

9

PEP 572:割り当て式のおかげで Python 3.8リリース(2019年10月20日予定)には新しいオプションがあります。新しい代入式演算子を使用すると、の結果を代入し、それを使用してを呼び出すことができます。結合されたコードは、2つのステートメントではなく単一の式になり、変更されます。:=copyupdate

newdict = dict1.copy()
newdict.update(dict2)

に:

(newdict := dict1.copy()).update(dict2)

すべての方法で同じように動作しながら。結果も返す必要がある場合dict(を返す式を要求しましたdict;上記はに作成して代入しnewdictますが、それを返さないため、これを使用して関数に引数をそのまま渡すことはできません、la myfunc((newdict := dict1.copy()).update(dict2))) 、次にor newdict最後に追加するだけです(これはをupdate返すためNone、これは誤りですので、評価されnewdict、式の結果として返されます)。

(newdict := dict1.copy()).update(dict2) or newdict

重要な警告:一般に、私はこのアプローチを推奨しません:

newdict = {**dict1, **dict2}

解凍のアプローチはより明確になり(最初に一般的な解凍を知っている人なら、それを行う必要があります)、結果に名前を付ける必要はまったくありません(そのため、すぐに一時ファイルを作成する場合は、より簡潔になります。関数またはlist/ tupleリテラルなどに含まれる)、そしてほぼ確実に高速であり、(CPythonでは)ほぼ以下と同等です。

newdict = {}
newdict.update(dict1)
newdict.update(dict2)

しかし、具体的なdictAPI を使用してC層で行われるため、動的メソッドルックアップ/バインディングや関数呼び出しのディスパッチオーバーヘッドは発生し(newdict := dict1.copy()).update(dict2)ません(動作が元の2ライナーと同じになることは避けられず、動的ルックアップを使用して個別の手順で作業を実行します) /バインディング/メソッドの呼び出し。

また、3つdictのs をマージすることは明らかであるため、より拡張性があります。

 newdict = {**dict1, **dict2, **dict3}

割り当て式を使用しても、そのようにスケーリングされません。あなたが得ることができる最も近いものは:

 (newdict := dict1.copy()).update(dict2), newdict.update(dict3)

またはNonesの一時的なタプルはありませんが、各None結果の真偽テストを行います。

 (newdict := dict1.copy()).update(dict2) or newdict.update(dict3)

これらのいずれも明らかにはるかに醜いであり、さらに非効率性を含む(いずれかの無駄な一時的tupleNoneコンマ分離、またはそれぞれの無意味truthiness試験のためのS updateNoneためのリターンor分離)。

代入式アプローチの唯一の真の利点は、次の場合に発生します。

  1. setdictの両方を処理する必要がある一般的なコードがあります(両方ともとをサポートcopyしているupdateため、コードは期待どおりに大まかに機能します)。
  2. dict自分自身だけでなく、任意のdictのようなオブジェクトを受け取ることを期待し、左側のタイプとセマンティクスを(プレーンで終わるのではなく)保存する必要がありdictます。動作するmyspecialdict({**speciala, **specialb})可能性はありますが、追加の一時的なが含まれ、プレーンが保持できない機能があるdict場合(たとえば、通常のは、キーの最初の出現に基づいて順序を保持し、キーの最後の出現に基づいて値を保持します。最後に基づいて順序を維持するものmyspecialdictdictdictキーの外観なので、値を更新するとキーも最後に移動します)。その場合、セマンティクスは正しくありません。割り当て式のバージョンは名前付きメソッドを使用するため(おそらく適切に動作するようにオーバーロードされています)、(一時的には回避しながら)を作成せdictず(dict1既にだった場合を除きdict)、元の型(および元の型のセマンティクス)を保持します。

8
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> x, z = dict(x), x.update(y) or x
>>> x
{'a': 1, 'b': 2}
>>> y
{'c': 11, 'b': 10}
>>> z
{'a': 1, 'c': 11, 'b': 10}

このメソッドはx、そのコピーで上書きします。xが関数の引数の場合、これは機能しません(例を参照)
bartolo-otrit
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.