6165

私は 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()私が探しているものです。)

4

56 に答える 56

7877

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

辞書xおよびyの場合、は、 からの値を置換zした値で浅くマージされた辞書になります。yx

  • Python 3.9.0 以降 (2020 年 10 月 17 日リリース):ここで説明されているPEP -584が実装され、最も簡単な方法が提供されます。

    z = x | y          # NOTE: 3.9+ ONLY
    
  • Python 3.5 以降の場合:

    z = {**x, **y}
    
  • Python 2 (または 3.4 以前) では、関数を記述します。

    def merge_two_dicts(x, y):
        z = x.copy()   # start with keys and values of x
        z.update(y)    # modifies z with keys and values of y
        return z
    

    そしていま:

    z = merge_two_dicts(x, y)
    

説明

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

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 で利用可能な古典的な Pythonic の方法は、これを 2 段階のプロセスとして行うことです。

z = x.copy()
z.update(y) # which returns None since it mutates z

どちらのアプローチでも、が 2 番目に来て、その値がのy値に置き換わるため、最終結果でがポイントされます。xb3

まだ Python 3.5 ではありませんが、単一の式が必要です

まだ Python 3.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

この関数は、すべての辞書に対して Python 2 および 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代わりに from の値が保持されます。x

>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}

使用してはいけない別のハック:

z = dict(x, **y)

これはdictコンストラクターを使用し、非常に高速でメモリ効率が高くなります (2 段階のプロセスよりもわずかに優れています)。 )、読みにくく、意図した使用法ではないため、Pythonic ではありません。

django で修正されている使用例を次に示します。

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

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

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

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)、辞書の仕様に沿っています。Python 2 と 3 の両方で機能します。これが文字列キーに対してのみ機能するという事実は、キーワード パラメーターがどのように機能するかの直接的な結果であり、dict の短所ではありません。また、この場所で ** 演算子を使用するのはメカニズムの悪用でもありません。実際、** は辞書をキーワードとして渡すように正確に設計されています。

繰り返しますが、キーが文字列でない場合、3 では機能しません。暗黙の呼び出し契約では、名前空間は通常の辞書を使用しますが、ユーザーは文字列であるキーワード引数のみを渡す必要があります。他のすべての callable はそれを強制しました。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 つの式で、最初の値が 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: {}}}

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

パフォーマンスは低いが正しいアドホック

これらのアプローチはパフォーマンスが低下しますが、正しい動作を提供します。より高い抽象化レベルで各キーと値のペアを反復処理するため、andまたは新しいアンパックよりもパフォーマンスが大幅に低下しますが、優先順位尊重されます (後者の辞書が優先されます)。copyupdate

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()) # 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

辞書に関するリソース

于 2014-11-10T22:11:48.750 に答える
1734

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

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

これは、あなたが望むように、最終的な dict を に置き、zkey の値を2 番目の ( ) dict の値bで適切に上書きします:y

>>> 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}
于 2008-09-02T07:50:12.600 に答える
705

別の方法:

z = x.copy()
z.update(y)
于 2008-09-02T13:00:46.707 に答える
404

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

z = dict(x, **y)

y: これは一般的な回答になりましたが、文字列以外のキーがある場合、これがまったく機能するという事実は CPython 実装の詳細の悪用であり、Python 3 では機能しないことを指摘することが重要です。または PyPy、IronPython、または Jython で。また、グイドはファンではありません。したがって、この手法は、前方互換性または相互実装の移植可能なコードにはお勧めできません。つまり、完全に回避する必要があるということです。

于 2008-09-02T15:52:07.777 に答える
249

これはおそらく一般的な答えではないでしょうが、ほぼ確実にこれをしたくないでしょう。マージのコピーが必要な場合は、 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() は古いバージョンで機能します。

于 2008-09-08T11:16:54.330 に答える
183

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

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

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

例 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 ステートメント バージョンの方がはるかに理解しやすいという彼の指摘はまったく正しいものです。

于 2008-10-23T02:38:56.143 に答える
174

Python 3.0 以降では、collections.ChainMapwhich グループ化された複数の辞書または他のマッピングを一緒に使用して、単一の更新可能なビューを作成できます。

>>> 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 = {'b':10, 'c': 11}
>>> {**x, **y}
{'a': 1, 'b': 10, 'c': 11}

Python 3.9 以降の更新: PEP 584ユニオン演算子を使用できます。

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> x | y
{'a': 1, 'b': 10, 'c': 11}
于 2013-04-28T03:15:38.467 に答える
148

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

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
于 2008-09-04T19:08:25.870 に答える
117

辞書を再帰的に/深く更新する

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

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

于 2011-11-29T11:52:15.537 に答える
94

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'}
于 2015-02-26T21:27:52.280 に答える
91

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

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()した場合、Python3でも機能します。items()

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

于 2010-10-14T18:55:15.583 に答える
89
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

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

于 2008-09-02T07:49:27.360 に答える
67

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

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

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

于 2011-10-14T16:12:33.527 に答える
62
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'}
于 2012-08-06T09:24:44.800 に答える
55

ラムダが悪だと思うなら、これ以上読む必要はありません。要求に応じて、高速でメモリ効率の高いソリューションを 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 行を使用するか、関数を記述することは、おそらくより良い方法です。

于 2011-11-23T18:08:23.420 に答える
48

パイソンになる。内包表記を使用します:

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

>>> print z
{'a': 1, 'c': 11, 'b': 10}
于 2016-01-20T11:46:22.490 に答える
42

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

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

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

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

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

編集:

dict(x, **y)Python 3 の場合、さらにいくつかのポイントがあります。まず、キーが文字列でない限り、このトリックは 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 のファンですが、ルックアップが多い場所ではあまり実用的ではないように見えます。

于 2013-10-09T18:09:08.627 に答える
33

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/を参照してください

于 2012-10-17T02:09:45.660 に答える
32

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

# 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}
于 2015-08-04T14:54:58.017 に答える
25

この浅いディクショナリに対する回答は適切でしたが、ここで定義されたメソッドはどれも、実際には深いディクショナリのマージを行いません。

例は次のとおりです。

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' を持つ必要があります。

chain を使用しても機能しません:

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

結果:

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

rcwesick が提供した深いマージでも同じ結果が得られます。

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

于 2012-08-03T23:36:50.823 に答える
23

変異しても構わないならx

x.update(y) or x

シンプルで読みやすく、高性能。常に を返しますが これは偽の値です。したがって、上記の式は、更新後に常に , に評価されます。update()Nonex

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

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

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

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

次の new walus オペレーターの使用法とそれほど違いはありませんが (Python 3.8+ のみ)、

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

特にデフォルトの引数を使用する場合:

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

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

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

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

于 2017-09-22T02:57:15.753 に答える
18

(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

于 2016-03-28T13:13:27.860 に答える
18

.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
于 2014-03-02T01:44:39.697 に答える
16
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

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

于 2015-11-30T13:04:00.003 に答える
13

これは、単一の辞書内包表記で実行できます。

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

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

于 2015-07-17T14:47:23.150 に答える
9
>>> 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}
于 2013-11-13T10:01:31.680 に答える
5

辞書内包表記を使用すると、

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

dc = {xi:(x[xi] if xi not in list(y.keys()) 
           else y[xi]) for xi in list(x.keys())+(list(y.keys()))}

与える

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

if else内包表記の構文に注意してください

{ (some_key if condition else default_key):(something_if_true if condition 
          else something_if_false) for key, value in dict_.items() }
于 2013-05-27T09:04:20.627 に答える
5

Python 3 の場合:

from collections import ChainMap
a = {"a":1, "b":2}
b = {"c":5, "d":8}
dict(ChainMap(a, b))  # {"a":1, "b":2, "c":5, "d":8}

両方の辞書に同じキーがある場合ChainMap、 は最初のキーの値を使用し、2 番目のキーの値を無視します。乾杯!

于 2019-05-31T17:10:27.723 に答える
4

OP の 2 つの辞書を結合すると、次のようになります。

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

具体的には、2 つのエンティティ (xおよび) の結合には、および/またはyのすべての要素が含まれます。残念ながら、投稿のタイトルにもかかわらず、OPが求めているのは組合ではありません。xy

以下の私のコードはエレガントでもワンライナーでもありませんが、ユニオンの意味と一致していると思います。

OPの例から:

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

z = {}
for k, v in x.items():
    if not k in z:
        z[k] = [(v)]
    else:
        z[k].append((v))
for k, v in y.items():
    if not k in z:
        z[k] = [(v)]
    else:
        z[k].append((v))

{'a': [1], 'b': [2, 10], 'c': [11]}

リストが必要かどうかは変更できますが、ディクショナリにいずれかのディクショナリの値としてリスト (およびネストされたリスト) が含まれている場合、上記は機能します。

于 2014-09-30T02:36:19.257 に答える
4

これに使えますtoolz.merge([x, y])

于 2016-11-18T12:53:29.477 に答える
2

ここで指定されていない解決策があります

z = {}
z.update(x) or z.update(y)

これは x だけでなく y も更新しません。パフォーマンス?極端に遅くなるとは思いません。

于 2013-12-05T08:02:05.070 に答える
2

2.5+ 用のハッキーなワンライナー:

>>> a = dict(x=2, y=3)
>>> b = dict(y=4, z=5)
>>> c = 'No Effect' if a.update(b) else a
>>> c
{'x': 2, 'y': 4, 'z': 5}

注意事項:

  • dict.updatedictインプレースを変更するため、次のように評価されますNone
  • A if C else BCは、 が最初に評価されます。こちらをご覧ください

したがって、ここでa.update(b)は が最初に評価され、 でa更新されb、操作の結果がNoneになるため、式は常に条件で指定された値else、つまりを返しますaaは既に変更されているため、更新された辞書である の新しい値を返しますa

改善

これはさらに改善することができ、古いバージョンでも動作するようにすることができます (おそらく python 1.0 も?):

>>> c = a.update(b) or a

ここでも、最初の部分は を生成するNoneため、常に 2 番目の部分を返しますが、更新操作は既に行われているため、常に更新された dict を返します。

批評

  • どちらのソリューションもの値を変更するaため、両方の入力辞書を変更せずに保持したい場合、これは良い考えではありません。

改善

のコピーaが必要な場合は、2 番目のものを少し変更できます。

>>> a = dict(x=2, y=3)
>>> b = dict(y=4, z=5)
>>> a, c = a.copy(), a.update(b) or a
>>> c
{'x': 2, 'y': 4, 'z': 5}
>>> d = dict(m=10, n=11)
>>> a, c = a.copy(), a.update(b) or a.update(d) or a
>>> c
{'x': 2, 'y': 4, 'z': 5, 'm': 10, 'n': 11}
>>> a
{'x': 2, 'y': 4}

注意事項

  • それ(特に最初のもの)は、2を超える任意の数のdictに対して醜く非現実的になります
  • さらに、これは明示的ではなく、非 Pythonicです。

これらのソリューションは非常に高速ですが、特にor新しい python 3.9 ユニオン演算子よりもおそらく高速なメソッド (完全にはわかりません。さらにテストが必要です。後で追加したい場合は歓迎します)、これらのメソッドはお勧めしません。上記の理由によります。完全を期すために追加しました。

于 2021-01-22T10:53:37.107 に答える
0

ディープ マージの方法はまだ見当たりませんでした (喜んで訂正します)。| を利用する newdictがデフォルト設定のセットであり、dictexistingが使用中の既存の設定のセットである場合の 3.9+ の演算子。私の目標は、newの既存の設定を上書きせずに、から追加された設定をマージすることでしたexisting。この再帰的な実装により、辞書を別の辞書からの新しい値でアップグレードできると思います。

これは SO への私の最初の真の小説の貢献の 1 つであるため、コメントを歓迎します。コードがうまくいかない場合は、喜んで修正します。

def merge_dict_recursive(new: dict, existing: dict):
    merged = new | existing

    for k, v in merged.items():
        if isinstance(v, dict):
            if k not in existing:
                # key is not in existing dict at all, so add entire value
                existing[k] = new[k]

            merged[k] = merge_dict_recursive(new[k], existing[k])
    return merged

テストデータの例:

new
{'dashboard': True,
 'depth': {'a': 1, 'b': 22222, 'c': {'d': {'e': 69}}},
 'intro': 'this is the dashboard',
 'newkey': False,
 'show_closed_sessions': False,
 'version': None,
 'visible_sessions_limit': 9999}
existing
{'dashboard': True,
 'depth': {'a': 5},
 'intro': 'this is the dashboard',
 'newkey': True,
 'show_closed_sessions': False,
 'version': '2021-08-22 12:00:30.531038+00:00'}
merged
{'dashboard': True,
 'depth': {'a': 5, 'b': 22222, 'c': {'d': {'e': 69}}},
 'intro': 'this is the dashboard',
 'newkey': True,
 'show_closed_sessions': False,
 'version': '2021-08-22 12:00:30.531038+00:00',
 'visible_sessions_limit': 9999}
于 2021-08-22T16:07:45.127 に答える