Domanda

Ho due dizionari Python e voglio scrivere una singola espressione che restituisca questi due dizionari uniti.IL update() Il metodo sarebbe ciò di cui ho bisogno, se restituisse il risultato invece di modificare un dict sul posto.

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

Come posso inserire il dict finale unito z, non x?

(Per essere più chiari, l'ultimo vince nella gestione dei conflitti dict.update() è quello che sto cercando anch'io.)

È stato utile?

Soluzione

Come posso unire due dizionari Python in un'unica espressione?

Per i dizionari x E y, z diventa un dizionario poco profondo con valori da y sostituendo quelli da x.

  • In Python 3.5 o versione successiva:

    z = {**x, **y}
    
  • In Python 2, (o 3.4 o precedente) scrivi una funzione:

    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
    

    e adesso:

    z = merge_two_dicts(x, y)
    

Spiegazione

Supponiamo che tu abbia due dict e desideri unirli in un nuovo dict senza alterare i dict originali:

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

Il risultato desiderato è ottenere un nuovo dizionario (z) con i valori uniti e i valori del secondo dict sovrascrivendo quelli del primo.

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

Una nuova sintassi per questo, proposta in PEP 448 E disponibile a partire da Python 3.5, È

z = {**x, **y}

Ed è infatti un'unica espressione.

Nota che possiamo unirci anche con la notazione letterale:

z = {**x, 'foo': 1, 'bar': 2, **y}

e adesso:

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

Ora viene visualizzato come implementato nel file programma di rilascio per 3.5, PEP 478, e ora si è fatto strada Novità in Python 3.5 documento.

Tuttavia, poiché molte organizzazioni utilizzano ancora Python 2, potresti voler farlo in modo compatibile con le versioni precedenti.Il modo classico di Python, disponibile in Python 2 e Python 3.0-3.4, consiste nel farlo come un processo in due fasi:

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

In entrambi gli approcci, y arriverà secondo e i suoi valori sostituiranno xi valori, quindi 'b' indicherà 3 nel nostro risultato finale.

Non ancora su Python 3.5, ma voglio a singola espressione

Se non sei ancora su Python 3.5 o hai bisogno di scrivere codice compatibile con le versioni precedenti e lo desideri in a singola espressione, l'approccio più efficace e corretto è inserirlo in una funzione:

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

e poi hai una sola espressione:

z = merge_two_dicts(x, y)

Puoi anche creare una funzione per unire un numero indefinito di dict, da zero a un numero molto grande:

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

Questa funzione funzionerà in Python 2 e 3 per tutti i dict.per esempio.dati i dettami a A g:

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

e coppie chiave-valore in g avrà la precedenza sui dicts a A f, e così via.

Critiche ad altre risposte

Non utilizzare ciò che vedi nella risposta precedentemente accettata:

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

In Python 2, crei due elenchi in memoria per ciascun dict, crei un terzo elenco in memoria con una lunghezza uguale alla lunghezza dei primi due messi insieme, quindi scarti tutti e tre gli elenchi per creare il dict. In Python 3, questo fallirà perché ne stai aggiungendo due dict_items oggetti insieme, non due elenchi -

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

e dovresti crearli esplicitamente come elenchi, ad es. z = dict(list(x.items()) + list(y.items())).Questo è uno spreco di risorse e potenza di calcolo.

Allo stesso modo, prendendo l'unione di items() in Python 3 (viewitems() in Python 2.7) fallirà anche quando i valori sono oggetti che non possono essere sottoposti a hash (come gli elenchi, ad esempio).Anche se i tuoi valori sono hashable, poiché gli insiemi sono semanticamente non ordinati, il comportamento non è definito per quanto riguarda la precedenza.Quindi non farlo:

>>> c = dict(a.items() | b.items())

Questo esempio dimostra cosa succede quando i valori non sono sottoposti a hash:

>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Ecco un esempio in cui y dovrebbe avere la precedenza, ma invece il valore di x viene mantenuto a causa dell'ordine arbitrario degli insiemi:

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

Un altro trucco da non usare:

z = dict(x, **y)

Questo utilizza il dict costruttore, ed è molto veloce ed efficiente in termini di memoria (anche leggermente più del nostro processo in due passaggi) ma a meno che tu non sappia esattamente cosa sta succedendo qui (cioè, il secondo dict viene passato come argomento di parola chiave al costruttore dict), è difficile da leggere, non è l'uso previsto e quindi non è Pythonic.

Ecco un esempio dell'utilizzo risolto in Django.

I dict sono destinati a prendere chiavi hashable (ad es.frozenset o tuple), ma questo metodo fallisce in Python 3 quando le chiavi non sono stringhe.

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

Dal lista di posta, Guido van Rossum, il creatore del linguaggio, scrisse:

Sto bene dichiarando dict ({}, ** {1: 3}) illegale, poiché dopo tutto è abusi del meccanismo **.

E

A quanto pare dict(x, **y) sta andando in giro come "cool hack" per "call x.update(y) e restituisce x".Personalmente lo trovo più spregevole di Bello.

Ciò che ho capito (così come quello del creatore della lingua) a cui è destinato l'uso dict(**y) serve per creare dict a fini di leggibilità, ad esempio:

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

invece di

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

Risposta ai commenti

Nonostante quello che dice Guido, dict(x, **y) è in linea con le specifiche dict, che tra l'altro.funziona sia per Python 2 che per 3.Il fatto che funzioni solo per le chiavi stringa è una conseguenza diretta del funzionamento dei parametri delle parole chiave e non un difetto di dict.Né usare l'operatore ** in questo posto è un abuso del meccanismo, infatti ** è stato progettato proprio per passare i dict come parole chiave.

Ancora una volta, non funziona per 3 quando le chiavi non sono stringhe.Il contratto di chiamata implicito prevede che gli spazi dei nomi accettino dettami ordinari, mentre gli utenti devono passare solo argomenti di parole chiave che siano stringhe.Tutti gli altri richiamabili lo hanno applicato. dict ha rotto questa coerenza in 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}

Questa incoerenza era grave date le altre implementazioni di Python (Pypy, Jython, IronPython).Pertanto è stato corretto in Python 3, poiché questo utilizzo potrebbe rappresentare una modifica sostanziale.

Ti sostengo che è un'incompetenza dannosa scrivere intenzionalmente codice che funziona solo in una versione di un linguaggio o che funziona solo con determinati vincoli arbitrari.

Altri commenti:

dict(x.items() + y.items()) è ancora la soluzione più leggibile per Python 2.La leggibilità conta.

La mia risposta: merge_two_dicts(x, y) in realtà mi sembra molto più chiaro, se siamo davvero preoccupati della leggibilità.E non è compatibile con le versioni successive, poiché Python 2 è sempre più deprecato.

{**x, **y} non sembra gestire dizionari nidificati.il contenuto delle chiavi nidificate viene semplicemente sovrascritto, non unito [...] Alla fine sono rimasto scottato da queste risposte che non si uniscono in modo ricorsivo e sono rimasto sorpreso che nessuno ne parlasse.Nella mia interpretazione della parola "unione" queste risposte descrivono "l'aggiornamento di un detto con un altro" e non la fusione.

SÌ.Devo rimandarti alla domanda, che richiede a poco profondo fusione di due dizionari, con i valori del primo sovrascritti da quelli del secondo, in un'unica espressione.

Supponendo due dizionari di dizionari, è possibile unirli ricorsivamente in un'unica funzione, ma è necessario fare attenzione a non modificare i dict da nessuna delle due fonti e il modo più sicuro per evitarlo è crearne una copia quando si assegnano valori.Poiché le chiavi devono essere hashable e di solito sono quindi immutabili, è inutile copiarle:

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

Utilizzo:

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

Trovare contingenze per altri tipi di valore va ben oltre lo scopo di questa domanda, quindi ti indicherò la mia risposta alla domanda canonica sulla "Unione dei dizionari dei dizionari".

Ad-hoc meno performanti ma corretti

Questi approcci sono meno performanti, ma forniranno un comportamento corretto.Essi saranno molto meno più performante di copy E update o il nuovo spacchettamento perché ripetono ciascuna coppia chiave-valore a un livello di astrazione più elevato, ma loro Fare rispettare l'ordine di precedenza (gli ultimi dettati hanno la precedenza)

Puoi anche concatenare i dict manualmente all'interno di una comprensione del dict:

{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7

o in Python 2.6 (e forse già nel 2.4 quando furono introdotte le espressioni del generatore):

dict((k, v) for d in dicts for k, v in d.items())

itertools.chain concatena gli iteratori sulle coppie chiave-valore nell'ordine corretto:

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

Analisi di performance

Eseguirò solo l'analisi delle prestazioni degli utilizzi noti per comportarsi correttamente.

import timeit

Quanto segue viene eseguito su Ubuntu 14.04

In Python 2.7 (sistema 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

In 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

Risorse sui dizionari

Altri suggerimenti

Nel tuo caso, quello che puoi fare è:

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

Questo inserirà, come desideri, il comando finale z, e imposta il valore per key b essere correttamente sovrascritto dal secondo (y) valore dict:

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

Se usi Python 3, è solo un po' più complicato.Creare z:

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

Un'alternativa:

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

Un'altra opzione, più concisa:

z = dict(x, **y)

Nota:questa è diventata una risposta popolare, ma è importante sottolinearlo se y ha chiavi non stringa, il fatto che funzioni è un abuso dei dettagli di implementazione di CPython e non funziona in Python 3 o in PyPy, IronPython o Jython.Anche, Guido non è un fan.Quindi non posso raccomandare questa tecnica per codice portatile compatibile con le versioni successive o con implementazione incrociata, il che significa in realtà che dovrebbe essere evitato del tutto.

Questa probabilmente non sarà una risposta popolare, ma quasi sicuramente non vorrai farlo.Se vuoi una copia che sia un'unione, usa copy (o deepcopy, a seconda di ciò che desideri) e quindi aggiornare.Le due righe di codice sono molto più leggibili - più pitoniche - rispetto alla creazione di una riga singola con .items() + .items().L'esplicito è meglio dell'implicito.

Inoltre, quando usi .items() (pre Python 3.0), stai creando un nuovo elenco che contiene gli elementi del dict.Se i tuoi dizionari sono grandi, ciò comporta un notevole sovraccarico (due grandi elenchi che verranno eliminati non appena verrà creato il dict unito).update() può funzionare in modo più efficiente, perché può eseguire il secondo dict elemento per elemento.

In termini di tempo:

>>> 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, il piccolo rallentamento tra i primi due ne vale la pena per la leggibilità.Inoltre, gli argomenti chiave per la creazione del dizionario sono stati aggiunti solo in Python 2.3, mentre copy() e update() funzioneranno nelle versioni precedenti.

In una risposta successiva, hai chiesto informazioni sulla prestazione relativa di queste due alternative:

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

Almeno sulla mia macchina (un x86_64 abbastanza ordinario con Python 2.5.2), alternativa z2 non è solo più breve e più semplice, ma anche significativamente più veloce.Puoi verificarlo tu stesso utilizzando il file timeit modulo fornito con Python.

Esempio 1:dizionari identici che associano 20 numeri interi consecutivi a se stessi:

% 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

z2 vince con un fattore di 3,5 circa.Dizionari diversi sembrano produrre risultati abbastanza diversi, ma z2 sembra sempre uscire in vantaggio.(Se ottieni risultati incoerenti per il file Stesso prova, prova a passare -r con un numero maggiore di quello predefinito 3.)

Esempio 2:dizionari non sovrapposti che associano 252 stringhe brevi a numeri interi e viceversa:

% 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 vince con un fattore 10 circa.Questa è una vittoria piuttosto grande nel mio libro!

Dopo aver confrontato questi due, mi sono chiesto se z1Le scarse prestazioni di potrebbero essere attribuite al sovraccarico di costruzione dei due elenchi di elementi, che a sua volta mi ha portato a chiedermi se questa variazione potrebbe funzionare meglio:

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

Alcuni test rapidi, ad es.

% 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

portami a concludere che z3 è un po' più veloce di z1, ma non così veloce come z2.Sicuramente non vale tutta la digitazione extra.

In questa discussione manca ancora qualcosa di importante, ovvero un confronto delle prestazioni di queste alternative con il modo "ovvio" di unire due elenchi:usando il update metodo.Per cercare di mantenere le cose su un piano di parità con le espressioni, nessuna delle quali modifica x o y, creerò una copia di x invece di modificarla sul posto, come segue:

z0 = dict(x)
z0.update(y)

Un risultato tipico:

% 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

In altre parole, z0 E z2 sembrano avere prestazioni sostanzialmente identiche.Pensi che questa potrebbe essere una coincidenza?Io non....

In effetti, arriverei al punto di affermare che è impossibile per il puro codice Python fare meglio di così.E se riesci a fare molto meglio in un modulo di estensione C, immagino che la gente di Python potrebbe essere interessata a incorporare il tuo codice (o una variazione del tuo approccio) nel core di Python.Python utilizza dict in molti posti;ottimizzare le proprie operazioni è un grosso problema.

Potresti anche scriverlo come

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

come fa Tony, ma (non sorprende) che la differenza nella notazione non abbia alcun effetto misurabile sull'esecuzione.Usa quello che ti sembra giusto.Naturalmente ha assolutamente ragione nel sottolineare che la versione a due affermazioni è molto più facile da capire.

Volevo qualcosa di simile, ma con la possibilità di specificare come unire i valori sulle chiavi duplicate, quindi l'ho modificato (ma non l'ho testato approfonditamente).Ovviamente questa non è una singola espressione, ma è una singola chiamata di funzione.

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

In Python 3, puoi usare collezioni.ChainMap che raggruppa più dict o altre mappature insieme per creare un'unica vista aggiornabile:

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

Aggiornamento ricorsivo/profondo di un 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

Dimostrazione:

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

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

print deepupdate(pluto_original, pluto_update)

Uscite:

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

Grazie Rednaw per le modifiche.

La versione migliore che potrei pensare senza usare la copia sarebbe:

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

È più veloce di dict(x.items() + y.items()) ma non così veloce come n = copy(a); n.update(b), almeno su CPython.Questa versione funziona anche in Python 3 se cambi iteritems() A items(), che viene eseguita automaticamente dallo strumento 2to3.

Personalmente preferisco questa versione perché descrive abbastanza bene ciò che voglio in un'unica sintassi funzionale.L'unico problema minore è che non è del tutto ovvio che i valori di y abbiano la precedenza sui valori di x, ma non credo che sia difficile capirlo.

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

Per gli elementi con chiavi in ​​entrambi i dizionari ("b"), puoi controllare quale finisce nell'output inserendolo per ultimo.

Python 3.5 (PEP 448) consente un'opzione di sintassi migliore:

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

O anche

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

Sebbene la domanda abbia già ricevuto risposta diverse volte, Questa semplice soluzione al problema non è stata ancora elencata.

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

È veloce quanto z0 e il malvagio z2 menzionato sopra, ma facile da capire e modificare.

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

new = dict_merge(old, extras)

Tra risposte così losche e dubbie, questo brillante esempio è l'unico buon modo per unire i dettami in Python, approvato dal dittatore a vita Guido van Rossum lui stesso!Qualcun altro ne ha suggerito la metà, ma non l'ha inserita in una funzione.

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

dà:

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

Se pensi che i lambda siano malvagi, non leggere oltre.Come richiesto, puoi scrivere la soluzione veloce ed efficiente in termini di memoria con un'espressione:

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}

Come suggerito sopra, usare due righe o scrivere una funzione è probabilmente la soluzione migliore.

Sii pitone.Usare un comprensione:

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

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

In Python3, il items metodo non restituisce più un elenco, ma piuttosto a visualizzazione, che funziona come un set.In questo caso dovrai prendere l'unione set dal momento della concatenazione con + non funzionerà:

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

Per un comportamento simile a Python3 nella versione 2.7, il file viewitems il metodo dovrebbe funzionare al posto di items:

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

Preferisco comunque questa notazione poiché sembra più naturale pensarla come un'operazione di unione di insiemi piuttosto che di concatenazione (come mostra il titolo).

Modificare:

Un paio di punti in più per Python 3.Innanzitutto, tieni presente che il dict(x, **y) il trucco non funzionerà in Python 3 a meno che le chiavi non siano inserite y sono stringhe.

Inoltre, Chainmap di Raymond Hettinger risposta è piuttosto elegante, poiché può prendere un numero arbitrario di dict come argomenti, ma dai documenti sembra che esamini in sequenza un elenco di tutti i dettami per ogni ricerca:

Le ricerche cercano successivamente le mappature sottostanti finché non viene trovata una chiave.

Questo può rallentarti se hai molte ricerche nella tua applicazione:

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

Quindi circa un ordine di grandezza più lento per le ricerche.Sono un fan di Chainmap, ma sembra meno pratico dove potrebbero esserci molte ricerche.

Abuso che porta a una soluzione con un'unica espressione per La risposta di Matteo:

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

Hai detto che volevi un'espressione, quindi ne ho abusato lambda per associare un nome e tuple per sovrascrivere il limite di una espressione di lambda.Sentiti libero di rabbrividire.

Potresti anche farlo ovviamente se non ti interessa copiarlo:

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

Soluzione semplice che utilizza itertools che preserva l'ordine (gli ultimi dettati hanno la precedenza)

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

Ed è l'utilizzo:

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

Due dizionari

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

N dizionari

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

sum ha prestazioni pessime.Vedere https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/

Anche se le risposte erano buone per questo poco profondo dizionario, nessuno dei metodi qui definiti esegue effettivamente un'unione profonda dei dizionari.

Seguono degli esempi:

a = { 'one': { 'depth_2': True }, 'two': True }
b = { 'one': { 'extra': False } }
print dict(a.items() + b.items())

Ci si aspetterebbe un risultato simile a questo:

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

Invece, otteniamo questo:

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

La voce "uno" avrebbe dovuto avere "profondità_2" e "extra" come elementi all'interno del dizionario se si fosse trattato veramente di un'unione.

Anche l'uso della catena non funziona:

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

Risultati in:

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

Anche la profonda fusione fornita da rcwesick crea lo stesso risultato.

Sì, funzionerà per unire i dizionari di esempio, ma nessuno di essi è un meccanismo generico da unire.Lo aggiornerò più tardi una volta che avrò scritto un metodo che esegua una vera unione.

Attingendo alle idee qui e altrove ho compreso una funzione:

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

Utilizzo (testato in 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})

Potresti usare invece una lambda.

Il problema che ho con le soluzioni elencate finora è che, nel dizionario unificato, il valore della chiave "b" è 10 ma, a mio modo di pensare, dovrebbe essere 12.Alla luce di ciò, presento quanto segue:

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

Risultati:

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

Questo può essere fatto con una singola comprensione del detto:

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

A mio avviso, la risposta migliore per la parte "espressione singola" poiché non sono necessarie funzioni aggiuntive ed è breve.

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

Questo dovrebbe risolvere il tuo problema.

(Solo per Python2.7*;ci sono soluzioni più semplici per Python3*.)

Se non sei contrario all'importazione di un modulo di libreria standard, puoi farlo

from functools import reduce

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

(IL or a po' nel lambda è necessario perché dict.update ritorna sempre None sul successo.)

E' così sciocco .update non restituisce nulla.
Utilizzo semplicemente una semplice funzione di supporto per risolvere il problema:

def merge(dict1,*dicts):
    for dict2 in dicts:
        dict1.update(dict2)
    return dict1

Esempi:

merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy

Usando una comprensione dict, puoi

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}

Nota la sintassi di if else nella comprensione

{ (some_key if condition else default_key):(something_if_true if condition 
          else something_if_false) for key, value in dict_.items() }

So che questo non si adatta perfettamente alle specifiche delle domande ("una battuta"), ma da allora nessuno delle risposte di cui sopra sono andate in questa direzione mentre moltissime risposte affrontavano il problema delle prestazioni, ho sentito che avrei dovuto contribuire con i miei pensieri.

A seconda del caso d'uso potrebbe non essere necessario creare un dizionario unito "reale" dei dizionari di input forniti.UN visualizzazione ciò che fa potrebbe essere sufficiente in molti casi, i.e.un oggetto che agisce Piace il dizionario unito lo farebbe senza calcolarlo completamente.Una versione pigra del dizionario unificato, per così dire.

In Python, questo è piuttosto semplice e può essere fatto con il codice mostrato alla fine del mio post.Detto questo, la risposta alla domanda iniziale sarebbe:

z = MergeDict(x, y)

Quando si utilizza questo nuovo oggetto, si comporterà come un dizionario unito ma avrà un tempo di creazione costante e un ingombro di memoria costante lasciando intatti i dizionari originali.Realizzarlo è decisamente più economico rispetto alle altre soluzioni proposte.

Naturalmente, se utilizzi molto il risultato, ad un certo punto raggiungerai il limite in cui la creazione di un vero dizionario unito sarebbe stata la soluzione più rapida.Come ho detto, dipende dal caso d'uso.

Se mai avessi sentito che preferiresti avere una vera fusione dict, quindi chiamando dict(z) lo produrrebbe (ma ovviamente molto più costoso delle altre soluzioni, quindi vale la pena menzionarlo).

Puoi anche usare questa classe per creare una sorta di dizionario copy-on-write:

a = { 'x': 3, 'y': 4 }
b = MergeDict(a)  # we merge just one dict
b['x'] = 5
print b  # will print {'x': 5, 'y': 4}
print a  # will print {'y': 4, 'x': 3}

Ecco il codice semplice di MergeDict:

class MergeDict(object):
  def __init__(self, *originals):
    self.originals = ({},) + originals[::-1]  # reversed

  def __getitem__(self, key):
    for original in self.originals:
      try:
        return original[key]
      except KeyError:
        pass
    raise KeyError(key)

  def __setitem__(self, key, value):
    self.originals[0][key] = value

  def __iter__(self):
    return iter(self.keys())

  def __repr__(self):
    return '%s(%s)' % (
      self.__class__.__name__,
      ', '.join(repr(original)
          for original in reversed(self.originals)))

  def __str__(self):
    return '{%s}' % ', '.join(
        '%r: %r' % i for i in self.iteritems())

  def iteritems(self):
    found = set()
    for original in self.originals:
      for k, v in original.iteritems():
        if k not in found:
          yield k, v
          found.add(k)

  def items(self):
    return list(self.iteritems())

  def keys(self):
    return list(k for k, _ in self.iteritems())

  def values(self):
    return list(v for _, v in self.iteritems())
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top