Come unisco i dizionari insieme in Python?


90
d3 = dict(d1, **d2)

Capisco che questo fonde il dizionario. Ma è unico? E se d1 ha la stessa chiave di d2 ma un valore diverso? Vorrei che d1 e d2 venissero unite, ma d1 ha la priorità se c'è una chiave duplicata.


9
Tieni presente che questo trucco è considerato un abuso del **passaggio di argomenti di parole chiave a meno che tutte le chiavi di non d2siano stringhe. Se non tutte le chiavi di d2sono stringhe, questo fallisce in Python 3.2 e in implementazioni alternative di Python come Jython, IronPython e PyPy. Vedi, ad esempio, mail.python.org/pipermail/python-dev/2010-April/099459.html .
Mark Dickinson

Risposte:


151

Puoi usare il .update()metodo se non hai più bisogno dell'originale d2:

Aggiorna il dizionario con le coppie chiave / valore di altre, sovrascrivendo le chiavi esistenti . Ritorno None.

Per esempio:

>>> d1 = {'a': 1, 'b': 2} 
>>> d2 = {'b': 1, 'c': 3}
>>> d2.update(d1)
>>> d2
{'a': 1, 'c': 3, 'b': 2}

Aggiornare:

Ovviamente puoi prima copiare il dizionario per crearne uno nuovo unito. Questo potrebbe o non potrebbe essere necessario. Nel caso in cui nel dizionario siano presenti oggetti composti (oggetti che contengono altri oggetti, come elenchi o istanze di classe), è copy.deepcopynecessario considerare anche questi.


1
In questo caso, gli elementi d1 dovrebbero ottenere correttamente la priorità se vengono trovate chiavi in ​​conflitto
Trey Hunner,

Nel caso ne avessi ancora bisogno, creane una copia. d3 = d2.copy () d3.update (d1) ma mi piacerebbe che d1 + d2 venisse aggiunto alla lingua.
stach

4
d1 + d2 è problematico perché un dizionario deve avere la priorità durante i conflitti e non è particolarmente ovvio quale.
rjh

d1 + d2 saranno implementati solo se Python ottiene una multimappa, altrimenti l'ambiguità per l'utente è troppo confusa per il guadagno di battitura a 8 byte.
Nick Bastin

Hai oggetti nel dizionario in questo esempio: isinstance(int, object) is Truema deepcopynon sembra necessario.
Antony Hatchkins

43

In Python2,

d1={'a':1,'b':2}
d2={'a':10,'c':3}

d1 sostituisce d2:

dict(d2,**d1)
# {'a': 1, 'c': 3, 'b': 2}

d2 sostituisce d1:

dict(d1,**d2)
# {'a': 10, 'c': 3, 'b': 2}

Questo comportamento non è solo un colpo di fortuna di implementazione; è garantito nella documentazione :

Se una chiave viene specificata sia nell'argomento posizionale che come argomento parola chiave, il valore associato alla parola chiave viene mantenuto nel dizionario.


3
I tuoi esempi falliranno (producendo un TypeError) in Python 3.2 e nelle versioni correnti di Jython, PyPy e IronPython: per quelle versioni di Python, quando passi un dict con la **notazione, tutte le chiavi di quel dict dovrebbero essere stringhe. Vedi il thread python-dev che inizia su mail.python.org/pipermail/python-dev/2010-April/099427.html per ulteriori informazioni.
Mark Dickinson

@Mark: grazie per l'avvertimento. Ho modificato il codice per renderlo compatibile con le implementazioni non CPython.
unutbu

3
fallisce se le tue chiavi sono tuple di stringhe e numeri. per es. d1 = {(1, 'a'): 1, (1, 'b'): 0,} d2 = {(1, 'a'): 1, (2, 'b'): 2, (2, 'a'): 1,}
MySchizoBuddy

Per quanto riguarda la sintassi di decompressione, vedere questo post per i cambiamenti imminenti in python 3.5.
Ioannis Filippidis

Stavo per dire che d = dict(**d1, **d2)funziona, ma è quello che fa riferimento a @IoannisFilippidis nel loro commento. Forse includere lo snippet qui sarebbe stato più chiaro, quindi eccolo qui.
dwanderson

14

Se vuoi d1avere la priorità nei conflitti, fai:

d3 = d2.copy()
d3.update(d1)

Altrimenti, invertire d2e d1.


1

La mia soluzione è definire una funzione di unione . Non è sofisticato e costa solo una riga. Ecco il codice in Python 3.

from functools import reduce
from operator import or_

def merge(*dicts):
    return { k: reduce(lambda d, x: x.get(k, d), dicts, None) for k in reduce(or_, map(lambda x: x.keys(), dicts), set()) }

Test

>>> d = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>> d_letters = {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d, d_letters)
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d_letters, d)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>> merge(d_letters)
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge()
{}

Funziona per un numero arbitrario di argomenti del dizionario. Se ci fossero chiavi duplicate in quel dizionario, la chiave dal dizionario più a destra nell'elenco degli argomenti vince.


1
Un semplice ciclo con una .updatechiamata in esso ( merged={}seguito da for d in dict: merged.update(d)) sarebbe più breve, più leggibile ed efficiente.
Mark Dickinson

1
Oppure, se si vuole veramente utilizzare reducee lambdas, come circa return reduce(lambda x, y: x.update(y) or x, dicts, {})?
Mark Dickinson

1
Puoi provare il tuo codice nella shell e vedere se è corretto. Quello che stavo cercando di fare è scrivere una funzione che può accettare diversi numeri di argomenti del dizionario con la stessa funzionalità. È meglio non usare x.update (y) sotto lambda, perché restituisce sempre None . E sto cercando di scrivere una funzione più generale merge_with che prenda un numero diverso di argomenti del dizionario e si occupi di chiavi duplicate con la funzione fornita. Una volta terminato, lo posterò in un altro thread in cui la soluzione è più pertinente.
Lei Zhao

Ecco il link dove ho scritto la soluzione più generale. Benvenuto e dai un'occhiata.
Lei Zhao


1

A partire da Python 3.9, l'operatore |crea un nuovo dizionario con le chiavi e i valori uniti da due dizionari:

# d1 = { 'a': 1, 'b': 2 }
# d2 = { 'b': 1, 'c': 3 }
d3 = d2 | d1
# d3: {'b': 2, 'c': 3, 'a': 1}

Questo:

Crea un nuovo dizionario d3 con le chiavi e i valori uniti di d2 e d1. I valori di d1 hanno la priorità quando d2 e d1 condividono le chiavi.


Notare anche l' |=operatore che modifica d2 unendo d1 in, con priorità sui valori d1:

# d1 = { 'a': 1, 'b': 2 }
# d2 = { 'b': 1, 'c': 3 }
d2 |= d1
# d2: {'b': 2, 'c': 3, 'a': 1}


0

Credo che, come detto sopra, l'utilizzo d2.update(d1)sia l'approccio migliore e che tu possa anche copiare d2prima se ne hai ancora bisogno.

Anche se, voglio sottolineare che in dict(d1, **d2)realtà è un brutto modo per unire i dizionari in generale poiché gli argomenti delle parole chiave devono essere stringhe, quindi fallirà se hai un dicttipo come:

{
  1: 'foo',
  2: 'bar'
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.