同じキー - python 辞書 結合 重複




2つの辞書を1つの式でマージするには? (20)

私は2つのPython辞書を持っており、これらの2つの辞書を返す単一の式を作成して結合したいと思います。 dictをインプレースで変更する代わりに、結果が返された場合は、 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}

xではなく、 zで最後にマージされたdictをどのように取得できますか?

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


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

辞書xyについては、 zyからの値をyからのy置き換えた結合された辞書になります。

  • Python 3.5以降では:

    z = {**x, **y}
    w = {'foo': 'bar', 'baz': 'qux', **y}  # merge a dict with literal values
    
  • 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)
    

説明

2つのディクテーションがあり、元のディクテーションを変更せずにそれらを新しいディクテーションにマージしたいとします。

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

望む結果は、値がマージされた新しい辞書( z )を取得し、第2の辞書の値が第1の辞書のそれを上書きすることである。

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

PEP 448で提案され、Python 3.5から利用できる新しい構文は次のとおりです。

z = {**x, **y}

そして、それは確かに一つの表現です。 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は2番目になり、その値はxの値に置き換えられます。したがって、最終結果では'b'3を指します。

まだPython 3.5ではなく、 単一の式が必要です

あなたがPython 3.5をまだ使用していないか、下位互換性のあるコードを書く必要があり、これを1 つの式に入れたいのであれば、最も効果的なアプローチは関数に入れることです:

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)

定義されていない数のdictを0から非常に大きな数にマージする関数を作ることもできます:

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とPython 3ではすべてのdictについて動作します。 例えば、与えられaからg

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

gキー値のペアはdictsよりも優先されます。

他の回答の批判

あなたが以前受け入れた答えに見られるものを使用しないでください:

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

Python 2では、各ディクショナリに対して2つのリストを作成し、最初の2つの長さに等しい長さのメモリ内に3つ目のリストを作成し、3つのリストをすべて破棄してディクショナリを作成します。 Python 3では、 2つのリストではなく、2つのdict_itemsオブジェクトを一緒に追加するので、これは失敗します。

>>> 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())) 。 これは、リソースと計算能力の無駄です。

同様に、Python 3(Python 2.7のviewitems() items()items()和集合を取ることは、値がハッシュ可能なオブジェクト(例えばリストなど)である場合にも失敗します。 値がハッシュ可能であっても、セットは意味的に順序付けされていないため、動作は優先順位に関して未定義です。 だからこれをしないでください:

>>> 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修復されている使用例です。

Dictsはハッシュ可能なキー(例: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

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

私はdict({}、** {1:3})を宣言しても問題ありません。

そして

どうやらdict(x、** y)は "x.update(y)を呼び出してxを返す"ための "クールなハック"として回っているようです。 個人的に私は涼しいよりも卑劣なことがわかります。

dict(**y)意図された使用法は、読みやすくするための辞書を作成することです。これは私の理解です( 言語作成者の理解もdict(**y)

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

の代わりに

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

コメントへの応答

Guidoが言っていることにもかかわらず、 dict(x, **y)はbtwのdict仕様に沿っています。 Python 2と3の両方で動作します。これは文字列キーに対してのみ機能するという事実は、キーワードパラメータの仕組みと短所ではない直接的な結果です。 この場所で**演算子を使用しても、実際に** dictsをキーワードとして渡すように設計されています。

ここでも、キーが非文字列の場合は3の場合は機能しません。 暗黙的な呼び出し規約では、名前空間は通常のdictを取りますが、ユーザーは文字列であるキーワード引数だけを渡す必要があります。 他のすべての呼び出し可能コードがそれを強制しました。 dictはPython 2でこの一貫性を破った:

>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}

この矛盾はPythonの他の実装(Pypy、Jython、IronPython)を考慮すると悪いものでした。 したがって、この使用法は大きな変更になる可能性があるため、Python 3で修正されました。

私は、言語のあるバージョンでしか動作しないコードを意図的に作成する悪意のある無能さであること、または特定の任意の制約が与えられた場合にのみ動作することをあなたに提出します。

別のコメント:

dict(x.items() + y.items())は、Python 2にとって最も読みやすいソリューションです。

私の応答: merge_two_dicts(x, y)実際に私たちが読みやすさを心配している場合、私にとってははるかに明確です。 また、Python 2はますます非難されているため、前方互換性はありません。

パフォーマンスは低下するが正しいAd-hocs

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

また、ディクテーションをディクテーション内で手動でチェーンすることもできます。

{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(deadsnakes 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

辞書のリソース


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

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に感謝します。


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では、 collections.ChainMapを使用すると、複数のdictsやその他のマッピングをまとめて、単一の更新可能なビューを作成できます。

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

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

python3では、 itemsメソッドはリストを返さず 、セットのように動作するビューを 返します 。 この場合、 +連結するとうまくいかないので、集合体を取る必要があります:

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

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

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

私はこの表記法を好んで使用しています。なぜなら、それは連結(タイトルが示すように)ではなく組合演算を集合として考えることがより自然なように思えるからです。

編集:

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

また、Raymond HettingerのChainmapのanswerは、引数として任意の数のdictを取ることができるのでかなりエレガントですが、docsからは各参照のすべての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のファンですが、多くのルックアップがある場合はあまり実用的ではありません。



ここや他の場所でアイデアを描くと、私は関数を理解しました:

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

代わりにラムダを使うことができます。


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

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

time面で:

>>> 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()は古いバージョンでは機能します。


これまでに挙げた解決策の問題は、マージされた辞書では、キー "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}

ピジョンソニックです。 comprehension使用する:

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

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

マシューの答えのための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}

あなたは一つの表現がほしいと言ったので、 lambdaを使って名前を結びつけ、タプルはラムダの一表現の限界を上書きしてしまった。 気にしないでください。

もちろん、コピーを気にしなければ、これを行うこともできます:

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

ラムダが悪いと思ったら、それ以上読むことはありません。 要求に応じて、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行を使うか関数を書くことは、おそらくより良い方法です。


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

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

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

z = dict(x, **y)

:これは一般的な答えになっていますが、 yに文字列以外のキーが含まれていれば、これはまったく動作するという事実はCPython実装の細部の悪用であり、Python 3では機能しません、PyPy、IronPython、またはJythonで実行されます。 また、 グイドはファンではありません 。 だから私はこの技術をフォワード互換またはクロスインプリメンテーションのポータブルコードに推奨することはできません。これは本当に完全に回避すべきであることを意味します。


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

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

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

個人的に私は、このバージョンが一番好きです。なぜなら、私が1つの機能的な構文で必要としているものをかなり良く説明しているからです。 わずかな問題は、yの値がxの値よりも優先されることを完全にはっきりさせることはできませんが、それを理解するのは難しいとは思われません。


答えがこの浅い辞書には良いとはいえ、ここで定義されている方法のどれも深い辞書マージを実際には行いません。

例は以下のとおりです:

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が与えた深いマージによっても同じ結果が得られます。

はい、サンプルディクショナリをマージすることはできますが、マージする一般的なメカニズムはありません。 真のマージを行うメソッドを書いたら、これを後で更新します。


(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


Python 3.5では**、新しい辞書を作成するためにunpack を使用することができます。この方法は過去の回答では示されていません。また、{}代わりに使う方が良いですdict()。なぜなら{}、Pythonのリテラルなのでdict()、関数呼び出しが必要です。

dict1 = {'a':1}
dict2 = {'b':2}
new_dict = {**dict1, **dict2}
>>>new_dict
{'a':1, 'a':2}

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

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

これはあなたの問題を解決するはずです。





merge