Come posso unire due dizionari Python in una singola espressione?
Per i dizionari x
e y
, z
diventa un dizionario poco unito con valori da y
sostituire quelli da x
.
In Python 3.5 o versioni successive:
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)
In Python 3.9.0a4 o versioni successive (data di rilascio finale circa ottobre 2020): PEP-584 , discusso qui , è stato implementato per semplificare ulteriormente questo:
z = x | y # NOTE: 3.9+ ONLY
Spiegazione
Supponi di avere due dicts e desideri unirli in un nuovo dict senza alterare i dicts 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 è davvero una singola 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 mostrato come implementato nel programma di rilascio per 3.5, PEP 478 , e ora si è fatto strada nel documento Novità di Python 3.5 .
Tuttavia, poiché molte organizzazioni sono ancora su Python 2, potresti volerlo fare in modo compatibile con le versioni precedenti. Il modo classico di Pythonic, disponibile in Python 2 e Python 3.0-3.4, consiste nel fare questo in due passaggi:
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 quelli x
, quindi 'b'
indicheranno il 3
nostro risultato finale.
Non ancora su Python 3.5, ma vuoi una 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 una singola espressione , l'approccio più performante e corretto è metterlo 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 dadi, 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 dicts. ad esempio dati forniti a
a g
:
z = merge_dicts(a, b, c, d, e, f, g)
e chiave a coppie di valori g
avranno precedenza su dicts a
a f
, e così via.
Critiche di altre risposte
Non usare ciò che vedi nella risposta precedentemente accettata:
z = dict(x.items() + y.items())
In Python 2, crei due elenchi in memoria per ogni 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é 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 potere di calcolo.
Allo stesso modo, prendere l'unione di items()
in Python 3 ( viewitems()
in Python 2.7) fallirà anche quando i valori sono oggetti non lavabili (come gli elenchi, per esempio). Anche se i tuoi valori sono hash, poiché gli insiemi sono semanticamente non ordinati, il comportamento è indefinito rispetto alla precedenza. Quindi non farlo:
>>> c = dict(a.items() | b.items())
Questo esempio dimostra cosa succede quando i valori non sono lavabili:
>>> 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 che non dovresti 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 (ovvero, il secondo dict viene passato come argomento di parole chiave al dict costruttore), è difficile da leggere, non è l'uso previsto e quindi non è Pythonic.
Ecco un esempio dell'uso corretto in django .
I Dicts sono destinati a prendere chiavi hash (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
Dalla mailing list , Guido van Rossum, il creatore della lingua, scrisse:
Sto bene dichiarando illegale dict ({}, ** {1: 3}), dal momento che dopo tutto si tratta di un abuso del meccanismo **.
e
Apparentemente dict (x, ** y) sta andando in giro come "cool hack" per "chiama x.update (y) e restituisce x". Personalmente lo trovo più spregevole che bello.
È mia comprensione (così come la comprensione del creatore della lingua ) che l'uso previsto dict(**y)
è per la creazione di dicts 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 ciò che dice Guido, dict(x, **y)
è in linea con le specifiche del dict, che tra l'altro. funziona sia per Python 2 che per 3. Il fatto che questo funzioni solo per le chiavi di stringa è una conseguenza diretta del funzionamento dei parametri delle parole chiave e non una breve introduzione di dict. Né sta usando l'operatore ** in questo luogo un abuso del meccanismo, infatti ** è stato progettato proprio per passare a parole chiave come parole chiave.
Ancora una volta, non funziona per 3 quando le chiavi sono non stringhe. Il contratto di chiamata implicito è che gli spazi dei nomi prendono dicts ordinari, mentre gli utenti devono solo passare argomenti di parole chiave che sono stringhe. Tutti gli altri callable lo hanno imposto. 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 negativa date altre implementazioni di Python (Pypy, Jython, IronPython). Quindi è stato risolto in Python 3, poiché questo utilizzo potrebbe essere un cambiamento radicale.
Sottolineo che è incompetenza malevola scrivere intenzionalmente codice che funziona solo in una versione di una lingua o che funziona solo a causa di determinati vincoli arbitrari.
Più commenti:
dict(x.items() + y.items())
è ancora la soluzione più leggibile per Python 2. La leggibilità conta.
La mia risposta: in merge_two_dicts(x, y)
realtà mi sembra molto più chiaro, se in realtà siamo preoccupati per la leggibilità. E non è compatibile con il futuro, poiché Python 2 è sempre più deprecato.
{**x, **y}
non sembra gestire dizionari nidificati. il contenuto delle chiavi nidificate viene semplicemente sovrascritto, non unito [...] Ho finito per essere bruciato da queste risposte che non si fondono in modo ricorsivo e sono rimasto sorpreso dal fatto che nessuno lo abbia menzionato. Nella mia interpretazione della parola "fusione" queste risposte descrivono "l'aggiornamento di un detto con un altro" e non la fusione.
Sì. Devo rimandarti alla domanda, che richiede una fusione superficiale di due dizionari, con i valori del primo che vengono sovrascritti dal secondo - in una singola espressione.
Supponendo due dizionari di dizionari, si potrebbe fonderli in modo ricorsivo in una singola funzione, ma si dovrebbe fare attenzione a non modificare i dicts da una delle due fonti, e il modo più sicuro per evitarlo è fare una copia quando si assegnano valori. Poiché le chiavi devono essere hash e di solito non sono modificabili, è 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
Uso:
>>> x = {'a':{1:{}}, 'b': {2:{}}}
>>> y = {'b':{10:{}}, 'c': {11:{}}}
>>> dict_of_dicts_merge(x, y)
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}
Presentare contingenze per altri tipi di valore va ben oltre lo scopo di questa domanda, quindi ti indicherò la mia risposta alla domanda canonica su una "Unione di dizionari di dizionari" .
Ad-hocs meno performanti ma corretti
Questi approcci sono meno performanti, ma forniranno un comportamento corretto. Saranno molto meno performante rispetto copy
e update
o la nuova disimballaggio perché iterazioni su ogni coppia di valori-chiave a un livello più elevato di astrazione, ma fare rispettare l'ordine di precedenza (quest'ultimo dicts prevalgono)
Puoi anche incatenare manualmente i dadi 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
incrocerà gli iteratori sulle coppie chiave-valore nell'ordine corretto:
import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))
Analisi di performance
Farò solo l'analisi delle prestazioni degli usi 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
z = x | y