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

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは、夫に会ったとき、典型的な交際のアドバイスに逆らいました。

マイケルシーンが非営利の俳優である理由

マイケルシーンが非営利の俳優である理由

マイケルシーンは非営利の俳優ですが、それは正確にはどういう意味ですか?

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

特徴的なスターのコリン・エッグレスフィールドは、RomaDrama Liveでのスリル満点のファンとの出会いについて料理しました!加えて、大会での彼のINSPIREプログラム。

「たどりつけば」をオンラインでストリーミングできない理由

「たどりつけば」をオンラインでストリーミングできない理由

ノーザンエクスポージャーが90年代の最も人気のある番組の1つになった理由を確認するには、Blu-rayまたはDVDプレーヤーをほこりで払う必要があります。

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

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

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

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖は、世界で2番目に大きいボイリング湖です。そこにたどり着くまでのトレッキングは大変で長いですが、努力する価値は十分にあります。

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

サロンからのヘアトリミングや個人的な寄付は、油流出を吸収して環境を保護するのに役立つマットとして再利用できます。

ホワイトハウスの最も記憶に残る結婚式を見てください

ホワイトハウスの最も記憶に残る結婚式を見てください

過去200年以上の間にホワイトハウスで結婚したのはほんの数人です。彼らは誰でしたか、そしてそこで結婚式を獲得するために何が必要ですか?

控訴裁判所によって取り下げられたメタン規制の施行を遅らせるEPAの試み

控訴裁判所によって取り下げられたメタン規制の施行を遅らせるEPAの試み

ゲッティ経由の画像。スコット・プルイットは環境保護庁の長であり、二酸化炭素とメタンガスとの愛情のこもった関係を持っていますが、彼は地球温暖化を信じることに冷たい肩を与えています。

メッセージングアプリの「誰かが入力している」バブルが実際に意味するもの

メッセージングアプリの「誰かが入力している」バブルが実際に意味するもの

一連の点滅する省略記号であろうと、「誰かが入力している」という明示的な通知であろうと、入力インジケーターと呼ばれるメッセージング機能は、多くの人にとって便利なツールであり、不安の原因でもあります。しかし、それがどのように機能するかについてもっと知ることは、少なくとも私たちにストレスを引き起こす推測ゲームを制限することができます。

マット・ラウアーは、離婚の和解で数百万ドルと馬の農場を失うことに「激怒」したと伝えられています

マット・ラウアーは、離婚の和解で数百万ドルと馬の農場を失うことに「激怒」したと伝えられています

マット・ラウアーと彼の妻のアネット・ロークは結婚間近であり、離婚手続きが激化していると伝えられています。ある情報筋は、ラウアーが「和解で約5,000万ドルをフォークする予定」であり、「マットは激怒している」と語った。彼は本質的に彼の純資産の半分をアネットに手渡している。

まあ、これは厄介です:レイプ告発者はケレンウィンズロージュニアの弁護士を彼女の攻撃者として特定します

まあ、これは厄介です:レイプ告発者はケレンウィンズロージュニアの弁護士を彼女の攻撃者として特定します

2018年6月15日金曜日、カリフォルニア州ビスタでの罪状認否の際、元NFLフットボール選手のケレンウィンズロージュニア(中央)が弁護士のブライアンワトキンス(左)とハーベイスタインバーグの後ろに立っています。

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya shared a sweet photo in honor of boyfriend Tom Holland's 26th birthday Wednesday

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

シーレン「Ms.JuicyBaby」ピアソンは、先月脳卒中で入院した後、「もう一度たくさんのことをする方法を学ばなければならない」ため、言語療法を受けていることを明らかにしました。

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

オスカー受賞者の世紀半ばの家には、3つのベッドルーム、2つのバス、オーシャンフロントの景色があります。

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、生後4か月の娘、モナコに母乳育児をしていると語った。

投資ノート:Bioscout AU$300万シード

投資ノート:Bioscout AU$300万シード

Bioscoutは、農家を運転席に置くという使命を負っています。Artesian(GrainInnovate)やUniseedと並んで、最新のシードラウンドでチームを支援できることをうれしく思います。問題真菌症による重大な作物の損失は、農民にとって試練であることが証明されています。

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

遠隔医療は、パンデミック後の時代では新しいものではなく、時代遅れの分野でもありません。しかし、業界を詳しく見ると、需要と供給の強力な持続可能性と、米国で絶え間ない革命となる強力な潜在的成長曲線を示しています。

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

2021年は、世界的なベンチャーキャピタル(VC)の資金調達にとって記録的な年でした。DealStreetAsiaによると、東南アジアも例外ではなく、この地域では年間で記録的な25の新しいユニコーンが採掘されました。

ムーアの法則を超えて

ムーアの法則を超えて

計算に対する私たちの欲求とムーアの法則が提供できるものとの間には、指数関数的に増大するギャップがあります。私たちの文明は計算に基づいています—建築と想像力の現在の限界を超える技術を見つけなければなりません。

Language