7080 Stimmen

Wie führe ich zwei Wörterbücher in einem einzigen Ausdruck zusammen (Vereinigung von Wörterbüchern)?

Ich möchte zwei Wörterbücher zu einem neuen Wörterbuch zusammenführen.

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

z = merge(x, y)

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

使用方法 x.update(y) modifiziert x anstelle eines neuen Wörterbuchs zu erstellen. Ich möchte auch, dass die Konfliktbehandlung des letzten Gewinners von dict.update() auch.

195voto

Raymond Hettinger Punkte 197261

In Python 3.0 und höher können Sie verwenden collections.ChainMap die mehrere Dicts oder andere Mappings zu einer einzigen, aktualisierbaren Ansicht zusammenfasst:

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

Update für Python 3.5 und höher : Sie können verwenden PEP 448 erweitertes Wörterbuch Einpacken und Auspacken. Das ist schnell und einfach:

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

Update für Python 3.9 und höher : Sie können die PEP 584 Gewerkschaftsvertreter:

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

6 Stimmen

Aber man sollte vorsichtig sein, während mit ChainMap gibt es einen Haken, dass, wenn Sie doppelte Schlüssel haben die Werte aus dem ersten Mapping verwendet werden und wenn Sie rufen eine del auf z.B. eine ChainMap c löscht die erste Abbildung dieses Schlüssels.

14 Stimmen

@Prerit Was sollte es sonst tun? Das ist die normale Art und Weise, wie verkettete Namespaces funktionieren. Denken Sie daran, wie $PATH in der Bash funktioniert. Das Löschen einer ausführbaren Datei im Pfad schließt eine andere ausführbare Datei mit demselben Namen weiter oben nicht aus.

4 Stimmen

@Raymond Hettinger Ich stimme zu und habe gerade eine Warnung hinzugefügt. Die meisten Leute wissen das vielleicht nicht :D

158voto

rcreswick Punkte 16005

Ich wollte etwas ähnliches, aber mit der Fähigkeit zu spezifizieren, wie die Werte auf doppelte Schlüssel zusammengeführt wurden, so hackte ich dies aus (aber nicht stark testen). Offensichtlich ist dies nicht ein einzelner Ausdruck, aber es ist ein einzelner Funktionsaufruf.

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

0 Stimmen

Praktische Lösung, wenn das Standardverhalten der kürzeren und einfacheren Lösungen (Ersetzung der Werte gemeinsamer Schlüssel durch das zweite Wörterbuch) nicht gewünscht ist. In Python 3 ist iteritems() in dicts nicht mehr verfügbar, und man kann stattdessen einfach items() verwenden.

130voto

Stan Punkte 3979

Rekursive/tiefe Aktualisierung eines Diktats

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

Demonstration:

pluto_original = {
    'name': 'Pluto',
    'details': {
        'tail': True,
        'color': 'orange'
    }
}

pluto_update = {
    'name': 'Pluutoo',
    'details': {
        'color': 'blue'
    }
}

print deepupdate(pluto_original, pluto_update)

Ausgänge:

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

Danke rednaw für die Änderungen.

3 Stimmen

Damit ist die Frage nicht beantwortet. Die Frage verlangt eindeutig nach einem neuen Wörterbuch, z, aus den ursprünglichen Wörterbüchern x und y, wobei die Werte von y die von x ersetzen - nicht nach einem aktualisierten Wörterbuch. Diese Antwort modifiziert y an Ort und Stelle, indem sie Werte aus x hinzufügt. Schlimmer noch, sie kopiert diese Werte nicht, so dass man das modifizierte Wörterbuch y weiter modifizieren könnte und die Änderungen sich im Wörterbuch x widerspiegeln könnten. @Jérôme Ich hoffe, dass dieser Code keine Fehler in Ihrer Anwendung verursacht - erwägen Sie zumindest die Verwendung von deepcopy zum Kopieren der Werte.

3 Stimmen

@AaronHall stimmt zu, dass dies keine Antwort auf die Frage ist. Aber es beantwortet mein Bedürfnis. Ich verstehe diese Einschränkungen, aber das ist in meinem Fall kein Problem. Wenn ich darüber nachdenke, ist der Name vielleicht irreführend, da er an eine Deepcopy denken lässt, die es nicht gibt. Aber es geht um tiefe Verschachtelung. Hier ist eine weitere Implementierung aus dem Martellibot: stackoverflow.com/questions/3232943/ .

124voto

Nico Schlömer Punkte 45358

Ich habe den vorgeschlagenen Benchmark mit Perfplot und stellte fest, dass die gute alte

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

ist die schnellste Lösung zusammen mit dem neuen (Python 3.9+)

x | y

enter image description here


Code zur Reproduktion des Plots:

from collections import ChainMap
from itertools import chain
import perfplot

def setup(n):
    x = dict(zip(range(n), range(n)))
    y = dict(zip(range(n, 2 * n), range(n, 2 * n)))
    return x, y

def copy_update(data):
    x, y = data
    temp = x.copy()
    temp.update(y)
    return temp

def add_items(data):
    x, y = data
    return dict(list(x.items()) + list(y.items()))

def curly_star(data):
    x, y = data
    return {**x, **y}

def chain_map(data):
    x, y = data
    return dict(ChainMap({}, y, x))

def itertools_chain(data):
    x, y = data
    return dict(chain(x.items(), y.items()))

def python39_concat(data):
    x, y = data
    return x | y

b = perfplot.bench(
    setup=setup,
    kernels=[
        copy_update,
        add_items,
        curly_star,
        chain_map,
        itertools_chain,
        python39_concat,
    ],
    labels=[
        "copy_update",
        "dict(list(x.items()) + list(y.items()))",
        "{**x, **y}",
        "chain_map",
        "itertools.chain",
        "x | y",
    ],
    n_range=[2 ** k for k in range(18)],
    xlabel="len(x), len(y)",
    equality_check=None,
)
b.save("out.png")
b.show()

104voto

Bilal Syed Hussain Punkte 7594

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

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

Oder sogar

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

In Python 3.9 verwenden Sie auch | und |= mit dem folgenden Beispiel aus PEP 584

d = {'spam': 1, 'eggs': 2, 'cheese': 3}
e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
d | e
# {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}

0 Stimmen

Inwieweit ist diese Lösung besser als die dict(x, **y) -Lösung? Wie Sie (@CarlMeyer) in der Anmerkung zu Ihrer eigenen Antwort erwähnt haben ( stackoverflow.com/a/39858/2798610 ) Guido ist der Meinung, dass diese Lösung illegal .

18 Stimmen

Guido mag nicht dict(x, **y) aus dem (sehr guten) Grund, dass es sich auf y nur Schlüssel haben, die gültige Schlüsselwortargumente sind (es sei denn, Sie verwenden CPython 2.7, wo der dict-Konstruktor schummelt). Dieser Einwand/diese Einschränkung gilt nicht für PEP 448, das die ** Entpackungssyntax zu Diktatliteralen. Diese Lösung hat also die gleiche Prägnanz wie dict(x, **y) ohne den Nachteil.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X