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

5224
Carl Meyer 2008-09-02 21:44.

私は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

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

30 answers

6421
Aaron Hall 2014-11-11 12:11.

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

辞書xy、の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.0以上(2020年10月17発売)では:PEP-584は、ここで議論し、さらにこれを簡素化するために実装されました:
    z = x | y          # NOTE: 3.9+ ONLY
    

説明

2つの辞書があり、元の辞書を変更せずにそれらを新しい辞書にマージするとします。

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}

望ましい結果はz、値がマージされた新しいディクショナリ()を取得し、2番目のディクショナリの値が最初のディクショナリの値を上書きすることです。

>>> z
{'a': 1, 'b': 3, 'c': 4}

このための新しい構文は、PEP 448で提案され、Python3.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に実装されているように表示されており、Python3.5ドキュメントの新機能に組み込まれています。

ただし、多くの組織はまだPython 2を使用しているため、下位互換性のある方法でこれを実行することをお勧めします。Python2とPython3.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にはまだありませんが、単一の式が必要です

まだPython3.5を使用していない場合、または下位互換性のあるコードを作成する必要があり、これを単一の式で記述したい場合、正しいアプローチで最もパフォーマンスが高いのは、関数に配置することです。

def merge_two_dicts(x, y):
    """Given two dictionaries, 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 dictionaries, shallow copy and merge into a new dict,
    precedence goes to key value pairs in latter dictionaries.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

この関数は、すべての辞書のPython2および3で機能します。与えられた辞書を例えばaするにはg

z = merge_dicts(a, b, c, d, e, f, g) 

のキーと値のペアは、へのg辞書よりも優先されます。af

他の回答の批評

以前に受け入れられた回答に表示されているものを使用しないでください。

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

Python 2では、dictごとにメモリ内に2つのリストを作成し、最初の2つを合わせた長さに等しい長さの3番目のリストをメモリ内に作成し、3つすべてのリストを破棄してdictを作成します。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で修正されている使用法の例です。

辞書はハッシュ可能なキー(フリーズセットやタプルなど)を取得することを目的としていますが、キーが文字列でない場合、このメソッドはPython3で失敗します。

>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

メーリングリストから、言語の作成者であるGuido vanRossumは次のように書いています。

結局のところ、**メカニズムの乱用であるため、dict({}、** {1:3})を違法と宣言しても問題ありません。

そして

どうやらdict(x、** y)は、「x.update(y)を呼び出してxを返す」の「クールハック」として回っています。個人的には、かっこいいというより卑劣だと思います。

使用目的が読みやすさの目的で辞書を作成することであるというのは、私の理解(および言語の作成者の理解)ですdict(**y)。例:

dict(a=1, b=10, c=11)

の代わりに

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

コメントへの回答

Guidoの言うことにもかかわらずdict(x, **y)、dictの仕様と一致しています。Python 2と3の両方で機能します。これが文字列キーでのみ機能するという事実は、キーワードパラメータがどのように機能するかという直接的な結果であり、dictの短期間の結果ではありません。また、この場所で**演算子を使用してメカニズムを悪用することもありません。実際、**は辞書をキーワードとして渡すように正確に設計されています。

繰り返しますが、キーが文字列以外の場合、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)を考えると、この不整合はひどいものでした。したがって、この使用法は重大な変更になる可能性があるため、Python3で修正されました。

言語の1つのバージョンでのみ機能するコード、または特定の任意の制約が与えられた場合にのみ機能するコードを意図的に作成することは、悪意のある無能であることをお伝えします。

その他のコメント:

dict(x.items() + y.items()) はまだPython2の最も読みやすいソリューションです。読みやすさは重要です。

私の回答:merge_two_dicts(x, y)私たちが実際に読みやすさを心配しているのであれば、実際には私にははるかに明確に思えます。また、Python 2はますます非推奨になっているため、上位互換性はありません。

{**x, **y}ネストされた辞書を処理していないようです。ネストされたキーの内容は単純に上書きされ、マージされません[...]再帰的にマージされないこれらの回答に焼けてしまい、誰も言及しなかったことに驚きました。「マージ」という言葉の私の解釈では、これらの回答は「ある辞書を別の辞書に更新する」ことを表しており、マージではありません。

はい。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

dict内包表記内で辞書を手動でチェーンすることもできます。

{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7

またはPython2.6(およびジェネレータ式が導入されたときはおそらく2.4):

dict((k, v) for d in dicts for k, v in d.items()) # iteritems in Python 2

itertools.chain キーと値のペアでイテレータを正しい順序でチェーンします。

from itertools import chain
z = dict(chain(x.items(), y.items())) # iteritems in Python 2

パフォーマンス分析

正しく動作することがわかっている使用法のパフォーマンス分析のみを行います。(自己完結型なので、自分でコピーして貼り付けることができます。)

from timeit import repeat
from itertools import chain

x = dict.fromkeys('abcdefg')
y = dict.fromkeys('efghijk')

def merge_two_dicts(x, y):
    z = x.copy()
    z.update(y)
    return z

min(repeat(lambda: {**x, **y}))
min(repeat(lambda: merge_two_dicts(x, y)))
min(repeat(lambda: {k: v for d in (x, y) for k, v in d.items()}))
min(repeat(lambda: dict(chain(x.items(), y.items()))))
min(repeat(lambda: dict(item for d in (x, y) for item in d.items())))

Python 3.8.1では、NixOS:

>>> min(repeat(lambda: {**x, **y}))
1.0804965235292912
>>> min(repeat(lambda: merge_two_dicts(x, y)))
1.636518670246005
>>> min(repeat(lambda: {k: v for d in (x, y) for k, v in d.items()}))
3.1779992282390594
>>> min(repeat(lambda: dict(chain(x.items(), y.items()))))
2.740647904574871
>>> min(repeat(lambda: dict(item for d in (x, y) for item in d.items())))
4.266070580109954
$ uname -a
Linux nixos 4.19.113 #1-NixOS SMP Wed Mar 25 07:06:15 UTC 2020 x86_64 GNU/Linux

辞書に関するリソース

  • Pythonの辞書実装についての私の説明。3.6用に更新されました。
  • 辞書に新しいキーを追加する方法についての回答
  • 2つのリストを辞書にマッピングする
  • 辞書に関する公式のPythonドキュメント
  • The Dictionary Even Mightier -Pycon2017でのBrandonRhodesによる講演
  • 現代のPython辞書、素晴らしいアイデアの合流点-Pycon2017でのRaymondHettingerによる講演
1659
Thomas Vander Stichele 2008-09-02 21:50.

あなたの場合、あなたができることは次のとおりです。

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

これにより、必要に応じて、最後のdictがに配置され、zkeyの値bが2番目の(y)dictの値によって適切にオーバーライドされます。

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

Python 2を使用している場合は、list()呼び出しを削除することもできます。zを作成するには:

>>> z = dict(x.items() + 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)
{'a': 1, 'c': 11, 'b': 10}
660
Matthew Schinckel 2008-09-03 03:00.

別の方法:

z = x.copy()
z.update(y)
369
Carl Meyer 2008-09-03 05:52.

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

z = dict(x, **y)

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

230
Tony Meyer 2008-09-09 01:16.

これはおそらく一般的な答えではないでしょうが、あなたはほぼ間違いなくこれをしたくないでしょう。マージであるコピーが必要な場合は、コピー(または必要に応じてディープコピー)を使用してから更新します。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()は古いバージョンで機能します。

164
zaphod 2008-10-23 16:38.

フォローアップの回答で、次の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

それz3z1、よりいくらか速いが、ほど速くはないという結論に導きますz2。余分な入力をすべて行う価値はありません。

この議論にはまだ重要なことが欠けています。それは、これらの選択肢と、2つのリストをマージする「明白な」方法(updateメソッドの使用)とのパフォーマンスの比較です。xまたはyを変更しない式と同等の立場を保つために、次のように、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

言い換えれば、z0そしてz2本質的に同じパフォーマンスを持っているようです。これは偶然かもしれないと思いますか?私はしません...

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

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

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

Tonyと同じですが、(当然のことながら)表記法の違いはパフォーマンスに測定可能な影響を与えないことがわかりました。自分に合ったものを使用してください。もちろん、彼は2ステートメントバージョンの方がはるかに理解しやすいと指摘するのは絶対に正しいです。

141
Raymond Hettinger 2013-04-28 17:15.

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以降の更新:PEP448拡張辞書のパッキングとアンパックを使用できます。これは速くて簡単です:

>>> x = {'a':1, 'b': 2}
>>> y = y = {'b':10, 'c': 11}
>>> {**x, **y}
{'a': 1, 'b': 10, 'c': 11}
127
rcreswick 2008-09-05 09:08.

似たようなものが欲しかったのですが、重複キーの値をどのようにマージするかを指定できるので、これをハックしました(ただし、あまりテストしませんでした)。明らかに、これは単一の式ではありませんが、単一の関数呼び出しです。

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
101
Stan 2011-11-30 01:52.

再帰的/ディープアップデートディクテーション

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
    }
}

編集してくれてありがとうrednaw。

80
driax 2010-10-15 08:55.

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

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)。このバージョンは、に変更iteritems()するitems()とPython 3でも機能します。これは、2to3ツールによって自動的に行われます。

個人的には、このバージョンが最も気に入っています。これは、単一の機能構文で必要なものがかなり適切に記述されているためです。唯一の小さな問題は、yの値がxの値よりも優先されることを完全に明らかにしていないことですが、それを理解するのは難しいとは思いません。

77
Bilal Syed Hussain 2015-02-27 11:27.

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では、|も使用します。および| = PEP584の以下の例

d = {'spam': 1, 'eggs': 2, 'cheese': 3}
e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
d | e
# {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}
69
Greg Hewgill 2008-09-02 21:49.
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

両方の辞書( 'b')にキーがあるアイテムの場合、どちらを最後に配置するかによって、どちらが出力になるかを制御できます。

55
phobie 2011-10-15 06:12.

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

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

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

53
Sam Watkins 2012-08-06 23:24.
def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)

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

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

与える:

{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}
47
EMS 2011-11-24 08:08.

ラムダが悪だと思うなら、これ以上読みません。要求に応じて、次の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行を使用するか、関数を作成することは、おそらくより良い方法です。

40
Robino 2016-01-21 01:46.

pythonicである。理解を使用する:

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

>>> print z
{'a': 1, 'c': 11, 'b': 10}
37
beardc 2013-10-10 08:09.

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

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

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

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

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

編集:

Python 3には、さらにいくつかのポイントがあります。まず、dict(x, **y)Python 3では、キーyが文字列でない限り、このトリックは機能しないことに注意してください。

また、Raymond HettingerのChainmapの回答は、引数として任意の数のdictを受け取ることができるため、非常にエレガントですが、ドキュメントからは、ルックアップごとにすべてのdictのリストを順番に調べているように見えます。

ルックアップは、キーが見つかるまで、基になるマッピングを連続して検索します。

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

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のファンですが、ルックアップが多い場合はあまり実用的ではありません。

28
reubano 2015-08-05 04:54.

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

# py2
from itertools import chain, imap
merge = lambda *args: dict(chain.from_iterable(imap(dict.iteritems, args)))

# py3
from itertools import chain
merge = lambda *args: dict(chain.from_iterable(map(dict.items, 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}
27
Mathieu Larose 2012-10-17 16:09.

2つの辞書

def union2(dict1, dict2):
    return dict(list(dict1.items()) + list(dict2.items()))

n辞書

def union(*dicts):
    return dict(itertools.chain.from_iterable(dct.items() for dct in dicts))

sumパフォーマンスが悪い。見るhttps://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/

26
Claudiu 2013-08-08 11:23.

マシューの答えの1つの式の解決策につながる虐待:

>>> 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}
21
Thanh Lim 2012-08-04 13:36.

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

例は次のとおりです。

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が提供したディープマージでも、同じ結果が得られます。

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

17
gilch 2017-09-22 16:57.

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

x.update(y) or x

シンプルで読みやすく、パフォーマンスが高い。あなた update()常にNone、偽の値であるを返すことを知っています。したがって、上記の式はx、更新後、常にに評価されます。

(のような.update())標準ライブラリのほとんどのミューテーションメソッドはNone慣例により返されるので、この種のパターンはそれらでも機能します。ただし、この規則に従わないdictサブクラスまたはその他のメソッドを使用しorている場合は、左側のオペランドが返されることがありますが、これは希望どおりではない可能性があります。代わりに、タプル表示とインデックスを使用できます。これは、最初の要素が何に評価されるかに関係なく機能します(ただし、それほどきれいではありません)。

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

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

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

次の新しいセイウチ演算子の使用とそれほど違いはありませんが(Python 3.8以降のみ):

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

コピーが必要な場合x | yは、PEP584スタイルが3.9以降で最もPythonicです。古いバージョンをサポートする必要がある場合{**x, **y}は、3.5以降ではPEP448スタイルが最も簡単です。ただし、それが(古い)Pythonバージョンで利用できない場合は、letパターンもここで機能します。

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

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

14
kjo 2016-03-29 03:13.

(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

13
Bijou Trouvaille 2013-07-19 19:49.

ここや他の場所でアイデアを利用して、私は関数を理解しました:

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})

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

13
upandacross 2013-12-04 08:11.

これまでにリストされたソリューションで私が抱えている問題は、マージされた辞書では、キー「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}
13
GetFree 2014-03-02 15:44.

.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
12
reetesh11 2015-12-01 03:04.
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

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

12
ShadowRanger 2019-03-01 07:16.

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

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

に:

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

あらゆる点で同じように振る舞いながら。結果も返す必要がある場合dictdict;を返す式を要求した場合、上記はを作成して割り当てnewdictますが、返さないため、関数に引数をそのまま渡すために使用することはできませんでした、la myfunc((newdict := dict1.copy()).update(dict2))) 、そしてちょうど追加or newdict(以降最後までupdate戻っNonefalsyあり、それは、その後の評価と戻ります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ライナーと不可避的に同一であり、個別のステップで作業を実行します)メソッドの/ binding / invocation。

また、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試験のためのSupdateNoneためのリターンor分離)。

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

  1. setsとdictsの両方を処理する必要のある汎用コードがあります(どちらもとをサポートcopyしているupdateため、コードはおおよそ期待どおりに機能します)
  2. あなたはdictそれ自体だけでなく、任意のdictのようなオブジェクトを受け取ることを期待しており、(プレーンで終わるのではなく)左側のタイプとセマンティクスを保持する必要がありdictます。動作するmyspecialdict({**speciala, **specialb})かもしれませんが、余分な一時的なものが必要になり、プレーンが保持できない機能があるdict場合(たとえば、通常のsは、キーの最初の出現に基づいて順序を保持し、キーの最後の出現に基づいて値を保持するようになりました。キーの最後の出現に基づいて順序を保持するため、値を更新するとキーも最後に移動します)、セマンティクスが間違っています。代入式バージョンは名前付きメソッド(おそらく適切に動作するようにオーバーロードされている)を使用するため、一時的なものを避けながら、元の型(および元の型のセマンティクス)を保持しながら、を作成することはありません(すでにがそうである場合を除く)。myspecialdictdictdictdictdict1dict
10
RemcoGerlich 2015-07-18 04:47.

これは、単一のdictの理解で行うことができます。

>>> 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)
    }

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

9
John La Rooy 2013-11-14 00:01.
>>> 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}

Related questions

MORE COOL STUFF

「水曜日」シーズン1の中心には大きなミステリーがあります

「水曜日」シーズン1の中心には大きなミステリーがあります

Netflixの「水曜日」は、典型的な10代のドラマ以上のものであり、実際、シーズン1にはその中心に大きなミステリーがあります.

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ケイト・ミドルトンは、州の夕食会と州の訪問中にカミラ・パーカー・ボウルズからスポットライトを奪いたくなかった、と専門家は言う.

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンが、オリビア・ワイルドが彼女とハリー・スタイルズとの間の「難しい」が「非常に友好的」な分割を恒久的にすることを望んでいる理由を見つけてください.

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする 

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする&nbsp;

エリザベス女王の死後、ケイト・ミドルトンが舞台裏で「非常に困難な時期」を過ごしていたと伝えられている理由を調べてください.

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セント ヘレナ島のジェイコブズ ラダーは 699 段の真っ直ぐ上る階段で、頂上に到達すると証明書が発行されるほどの難易度です。

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

パンデミックは終わったかもしれないが、Covid-19 は終わっていない

パンデミックは終わったかもしれないが、Covid-19 は終わっていない

2021 年 6 月 8 日にニューヨーク市で開催された covid-19 パンデミックで亡くなった人々の命を偲び、祝うために、ネーミング ザ ロスト メモリアルズが主催するイベントと行進の最中に、グリーンウッド墓地の正門から記念碑がぶら下がっています。週末、ジョー・バイデン大統領は、covid-19 パンデミックの終息を宣言しました。これは、過去 2 年以上にわたり、公の場でそうするための長い列の中で最新のものです。

デビル・イン・オハイオの予告編は、エミリー・デシャネルもオハイオにいることを明らかにしています

デビル・イン・オハイオの予告編は、エミリー・デシャネルもオハイオにいることを明らかにしています

オハイオ州のエミリー・デシャネル みんな早く来て、ボーンズが帰ってきた!まあ、ショーボーンズではなく、彼女を演じた俳優. エミリー・デシャネルに最後に会ってからしばらく経ちました.Emily Deschanel は、長期にわたるプロシージャルな Bones の Temperance “Bones” Brennan としてよく知られています。

ドナルド・トランプはFBIのマー・ア・ラーゴ襲撃映像をリリースする予定ですか?

ドナルド・トランプはFBIのマー・ア・ラーゴ襲撃映像をリリースする予定ですか?

どうやら、ドナルド・トランプに近い人々は、今月初めにFBIによって家宅捜索された彼のMar-a-Lago財産からの映像を公開するよう彼に勧めています. 前大統領はテープを公開するかどうかを確認していませんが、息子はフォックス・ニュースにそうなるだろうと語った.

Andor は、他の Star Wars ショーから大きな距離を置きます。

Andor は、他の Star Wars ショーから大きな距離を置きます。

アンドールの一場面。数十年前、ジョージ・ルーカスがスター・ウォーズのテレビ番組を制作するのを妨げた主な理由は、お金でした。

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、子供向けのパズルの本の序文を書き、ジョージ王子、シャーロット王女、ルイ王子と一緒にテキストを読むと述べた.

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

Yak's Produce は、数十個のつぶれたメロンを野生動物のリハビリ専門家であるレスリー グリーンと彼女のルイジアナ州の救助施設で暮らす 42 匹の動物に寄付しました。

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

8 枚目のスタジオ アルバムのリリースに向けて準備を進めているデミ ロヴァートは、「スーパー グレート ガイ」と付き合っている、と情報筋は PEOPLE に確認しています。

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

イーサン プラスの誕生日のお祝いは、TLC のウェルカム トゥ プラスビルのシーズン 4 のフィナーレで、戦争中の母親のキム プラスと妻のオリビア プラスを結びつけました。

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

ホームオフィスのセットアップ術を極めよう!AppExert の開発者は、家族全員が一緒にいる場合でも、在宅勤務の技術を習得しています。祖父や曽祖父が共同家族で暮らしていた頃の記憶がよみがえりました。

2022 年、私たちのデジタル ライフはどこで終わり、「リアル ライフ」はどこから始まるのでしょうか?

20 年前のタイムトラベラーでさえ、日常生活におけるデジタルおよびインターネットベースのサービスの重要性に驚くことでしょう。MySpace、eBay、Napster などのプラットフォームは、高速化に焦点を合わせた世界がどのようなものになるかを示してくれました。

ニューロマーケティングの秘密科学

ニューロマーケティングの秘密科学

マーケティング担当者が人間の欲望を操作するために使用する、最先端の (気味が悪いと言う人もいます) メソッドを探ります。カートをいっぱいにして 3 桁の領収書を持って店を出る前に、ほんの数点の商品を買いに行ったことはありませんか? あなたは一人じゃない。

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

私たちが住んでいる世界を確実に理解するには、データが必要です。ただし、空間参照がない場合、このデータは地理的コンテキストがないと役に立たなくなる可能性があります。

Language