python with Wie verschmelzen zwei Wörterbücher in einem Ausdruck?




python merge dictionaries with same keys (24)

Ich habe zwei Python-Wörterbücher, und ich möchte einen einzigen Ausdruck schreiben, der diese beiden Wörterbücher zusammenführt. Die update() -Methode wäre das, was ich brauche, wenn sie ihr Ergebnis zurückliefert, anstatt ein Diktat an Ort und Stelle zu ändern.

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

Wie kann ich das letzte zusammengesetzte Diktat in z , nicht in x ?

(Um es noch deutlicher zu machen, ist es das, was ich mit dem Konfliktbewältigen von dict.update() letzter dict.update() habe.


(Nur für Python2.7 *; für Python3 * gibt es einfachere Lösungen.)

Wenn Sie ein Standardbibliothekmodul nicht importieren möchten, können Sie dies tun

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

(Das or aBit in lambdaist notwendig, da bei Erfolg dict.updateimmer wieder zurückgegeben wird None.)


Ich habe hier und anderswo auf Ideen gestützt und habe eine Funktion verstanden:

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}

Verwendung (getestet in Python 3):

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

Sie könnten stattdessen ein Lambda verwenden.


Seien Sie pythonisch. Verwenden Sie ein comprehension :

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

Für Python 2:

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

Für Python 3:

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

Es gibt Ausgabe: {'a': 1, 'c': 11, 'b': 10}


Dies kann mit einem einzigen Diktierverständnis erfolgen:

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

Aus meiner Sicht die beste Antwort für den "Einzelausdruck" - Teil, da keine zusätzlichen Funktionen benötigt werden, und dies ist kurz.


Das Problem, das ich bei den bisher aufgelisteten Lösungen habe, ist, dass im zusammengeführten Wörterbuch der Wert für Schlüssel "b" 10 ist, aber meiner Meinung nach sollte es 12 sein. In diesem Licht stelle ich Folgendes vor:

from functools import reduce

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

Ergebnisse:

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

Obwohl die Antworten für dieses flache Wörterbuch gut waren, führt keine der hier definierten Methoden tatsächlich eine tiefe Wörterbuchzusammenführung durch.

Beispiele folgen:

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

Man würde ein Ergebnis von so etwas erwarten:

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

Stattdessen bekommen wir das:

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

Der Eintrag "one" hätte "depth_2" und "extra" als Elemente in seinem Wörterbuch, wenn es wirklich eine Zusammenführung war.

Die Kette funktioniert auch nicht:

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

Ergebnisse in:

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

Die tiefe Verschmelzung, die rcwesick hervorbrachte, führt auch zu demselben Ergebnis.

Ja, es wird funktionieren, um die Beispielwörterbücher zusammenzuführen, aber keines von ihnen ist ein allgemeiner Mechanismus zum Zusammenführen. Ich werde das später aktualisieren, sobald ich eine Methode schreibe, die eine echte Zusammenführung durchführt.


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

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

Unter diesen zwielichtigen und zweifelhaften Antworten ist dieses leuchtende Beispiel der einzige gute Weg, um Diktaturen in Python zusammenzuführen, die vom Diktator für das Leben von Guido van Rossum selbst befürwortet werden! Jemand anderes schlug die Hälfte davon vor, legte es aber nicht in eine Funktion.

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

gibt:

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


Während die Frage bereits mehrmals beantwortet wurde, wurde diese einfache Lösung des Problems noch nicht aufgelistet.

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}

Es ist so schnell wie z0 und das oben erwähnte böse z2, aber leicht zu verstehen und zu ändern.


In Python 3 können Sie collections.ChainMap die mehrere Diktiere oder andere Zuordnungen zu einer einzelnen, aktualisierbaren Ansicht zusammenfasst:

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

In python3 gibt die items Methode keine Liste mehr zurück , sondern eine Ansicht , die wie eine Menge wirkt. In diesem Fall müssen Sie die Mengenvereinigung übernehmen, da das Verketten mit + nicht funktioniert:

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

Für python3-ähnliches Verhalten in Version 2.7 sollte die viewitems Methode anstelle von items funktionieren:

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

Ich bevorzuge diese Notation sowieso, da es natürlicher erscheint, sie als eine gewerkschaftliche Vereinigungsoperation zu betrachten, als eine Verkettung (wie der Titel zeigt).

Bearbeiten:

Noch ein paar Punkte für Python 3. Beachten Sie zunächst, dass der Trick dict(x, **y) in Python 3 nicht funktioniert, es sei denn, die Schlüssel in y sind Strings.

Die answer von Chainmap von Raymond Hettinger ist ziemlich elegant, da sie eine beliebige Anzahl von Diktaten als Argumente verwenden kann, aber aus den Dokumenten sieht es so aus, als würde sie sequentiell eine Liste aller Diktate für jede Suche durchsehen:

Suchvorgänge durchsuchen die zugrunde liegenden Zuordnungen nacheinander, bis ein Schlüssel gefunden wird.

Dies kann Sie verlangsamen, wenn Sie in Ihrer Anwendung viele Nachschläge haben:

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

Also um eine Größenordnung langsamer für Suchvorgänge. Ich bin ein Fan von Chainmap, sieht aber weniger praktisch aus, wo es viele Nachschlagewerke gibt.


Missbrauch führt zu einer Ein-Ausdruck-Lösung für Matthews Antwort :

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

Sie sagten, Sie wollten einen Ausdruck, also habe ich lambda missbraucht, um einen Namen zu binden, und Tupel, um die Grenze von Lambdas Ausdrücken zu überschreiben. Fühlen Sie sich frei, sich zusammenzucken.

Sie können dies natürlich auch tun, wenn Sie nicht daran interessiert sind, es zu kopieren:

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

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

Dies wird wahrscheinlich keine beliebte Antwort sein, aber Sie möchten dies höchstwahrscheinlich nicht. Wenn Sie eine Kopie wünschen, die eine Zusammenführung ist, verwenden Sie copy (oder deepcopy , je nachdem, was Sie möchten) und aktualisieren Sie dann. Die zwei Codezeilen sind viel besser lesbar - mehr Pythonic - als die einzeilige Erstellung mit .items () + .items (). Explicit ist besser als implizit.

Wenn Sie .items () (vor Python 3.0) verwenden, erstellen Sie außerdem eine neue Liste, die die Elemente aus dem Diktat enthält. Wenn Ihre Wörterbücher groß sind, ist das ziemlich viel Aufwand (zwei große Listen, die verworfen werden, sobald das zusammengeführte Diktat erstellt wird). update () kann effizienter arbeiten, da es das zweite Dikt für Element durchlaufen kann.

In time Hinsicht:

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

Die winzige Verlangsamung zwischen den ersten beiden ist es meiner Meinung nach für die Lesbarkeit wert. Darüber hinaus wurden Schlüsselwortargumente für die Wörterbucherstellung nur in Python 2.3 hinzugefügt, wohingegen copy () und update () in älteren Versionen funktionieren.


Python 3.5 (PEP 448) ermöglicht eine schönere Syntaxoption:

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

new = dict_merge(old, extras)

Oder auch

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

In Ihrem Fall können Sie Folgendes tun:

z = dict(x, **y)

Wenn Sie möchten, wird das letzte Diktat in z , und der Wert für Schlüssel b wird durch den Wert des zweiten ( y ) Diktats richtig überschrieben:

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

Wenn Sie Python 3 verwenden, ist es nur etwas komplizierter. z erstellen:

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

In Python 3.5 können Sie Unpack **verwenden, um ein neues Wörterbuch zu erstellen. Diese Methode wurde in früheren Antworten nicht gezeigt. Es ist auch besser, {}statt zu verwenden dict(). Weil {}ist ein Python-Literal und dict()beinhaltet einen Funktionsaufruf.

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

Wenn Sie denken, dass Lambdas böse sind, lesen Sie nicht weiter. Auf Wunsch können Sie die schnelle und speichereffiziente Lösung mit einem Ausdruck schreiben:

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

Wie oben vorgeschlagen, ist die Verwendung von zwei Zeilen oder das Schreiben einer Funktion wahrscheinlich der bessere Weg.


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

Bei Elementen mit Schlüsseln in beiden Wörterbüchern ('b') können Sie steuern, welche in der Ausgabe landen, indem Sie diese zuletzt setzen.


Ich wollte etwas Ähnliches, aber mit der Möglichkeit, anzugeben, wie die Werte für doppelte Schlüssel zusammengeführt wurden, habe ich dies herausgehackt (aber nicht ausgiebig getestet). Offensichtlich ist dies kein einzelner Ausdruck, sondern ein einzelner Funktionsaufruf.

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

Eine Alternative:

% 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

Rekursives / tiefes Update eines Diktats

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

Demonstration:

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

Ausgänge:

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

Vielen Dank für die Änderungen.






merge