Jak połączyć słowniki w Pythonie?


91
d3 = dict(d1, **d2)

Rozumiem, że powoduje to scalenie słownika. Ale czy jest wyjątkowy? A co jeśli d1 ma ten sam klucz co d2, ale inną wartość? Chciałbym, aby d1 i d2 zostały połączone, ale d1 ma priorytet, jeśli istnieje zduplikowany klucz.


9
Należy pamiętać, że ta sztuczka jest uważana za nadużycie **przekazywania argumentów słów kluczowych, chyba że wszystkie klucze d2są ciągami. Jeśli nie wszystkie klucze d2są ciągami znaków, nie powiedzie się to w Pythonie 3.2 oraz w alternatywnych implementacjach Pythona, takich jak Jython, IronPython i PyPy. Zobacz na przykład mail.python.org/pipermail/python-dev/2010-April/099459.html .
Mark Dickinson

Odpowiedzi:


154

Możesz użyć tej .update()metody, jeśli nie potrzebujesz już oryginału d2:

Zaktualizuj słownik za pomocą par klucz / wartość z innych, nadpisując istniejące klucze . Wróć None.

Na przykład:

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

Aktualizacja:

Oczywiście możesz najpierw skopiować słownik, aby utworzyć nowy, scalony. Może to być konieczne lub nie. W przypadku, gdy w słowniku znajdują się obiekty złożone (obiekty, które zawierają inne obiekty, takie jak listy lub instancje klas), copy.deepcopynależy również wziąć pod uwagę.


1
W tym przypadku elementy d1 powinny poprawnie uzyskać priorytet, jeśli zostaną znalezione sprzeczne klucze
Trey Hunner

Jeśli nadal go potrzebujesz, po prostu zrób kopię. d3 = d2.copy () d3.update (d1), ale chciałbym, aby do języka dodano d1 + d2.
stach

4
d1 + d2 jest problematyczne, ponieważ jeden słownik musi mieć pierwszeństwo podczas konfliktów, a nie jest szczególnie oczywiste, który z nich.
rjh

d1 + d2 zostanie zaimplementowane tylko wtedy, gdy Python uzyska multimapę, w przeciwnym razie niejednoznaczność dla użytkownika jest zbyt zagmatwana dla 8-bajtowego wzmocnienia pisania.
Nick Bastin

W tym przykładzie masz obiekty w słowniku: isinstance(int, object) is Trueale deepcopynie wydaje się to konieczne.
Antony Hatchkins

43

W Pythonie2

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

d1 zastępuje d2:

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

d2 zastępuje d1:

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

Takie zachowanie to nie tylko przypadek implementacji; w dokumentacji gwarantuje się :

Jeśli klucz jest określony zarówno w argumencie pozycyjnym, jak i jako argument słowa kluczowego, wartość skojarzona ze słowem kluczowym jest zachowywana w słowniku.


3
Twoje przykłady zakończą się niepowodzeniem (tworząc TypeError) w Pythonie 3.2 oraz w aktualnych wersjach Jython, PyPy i IronPython: dla tych wersji Pythona, podczas przekazywania dyktu z **notacją, wszystkie klucze tego dyktu powinny być ciągami. Zobacz wątek python-dev zaczynający się na mail.python.org/pipermail/python-dev/2010-April/099427.html, aby uzyskać więcej informacji.
Mark Dickinson

@Mark: Dzięki za ostrzeżenie. Zmodyfikowałem kod, aby był zgodny z implementacjami innymi niż CPython.
unutbu

3
nie powiedzie się, jeśli twoje klucze są krotkami łańcuchów i liczb. np. d1 = {(1, 'a'): 1, (1, 'b'): 0,} d2 = {(1, 'a'): 1, (2, 'b'): 2, (2, 'a'): 1,}
MySchizoBuddy

Jeśli chodzi o składnię rozpakowywania, zobacz ten post, aby zapoznać się ze zmianami nadchodzącymi w Pythonie 3.5.
Ioannis Filippidis

Chciałem powiedzieć, że to d = dict(**d1, **d2)działa, ale do tego odwołuje się @IoannisFilippidis w swoim komentarzu. Być może włączenie tutaj fragmentu byłoby jaśniejsze, więc oto jest.
dwanderson

14

Jeśli chcesz d1mieć pierwszeństwo w konfliktach, zrób:

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

W przeciwnym razie odwróć d2i d1.


1

Moim rozwiązaniem jest zdefiniowanie funkcji scalania . To nie jest skomplikowane i kosztuje tylko jedną linię. Oto kod w Pythonie 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()) }

Testy

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

Działa dla dowolnej liczby argumentów słownikowych. Gdyby w tym słowniku były jakieś zduplikowane klucze, wygrywa klucz ze słownika znajdującego się najbardziej po prawej stronie na liście argumentów.


1
Prosta pętla z .updatewywołaniem ( merged={}po którym następuje for d in dict: merged.update(d)) byłaby krótsza, bardziej czytelna i wydajniejsza.
Mark Dickinson,

1
A jeśli naprawdę chcesz użyć reducei lambdas, co powiesz na to return reduce(lambda x, y: x.update(y) or x, dicts, {})?
Mark Dickinson,

1
Możesz wypróbować swój kod w powłoce i sprawdzić, czy jest poprawny. To, co próbowałem zrobić, to napisać funkcję, która może przyjmować różną liczbę argumentów słownikowych o tej samej funkcjonalności. Lepiej nie używać x.update (y) pod lambdą, ponieważ zawsze zwraca None . I próbuję napisać bardziej ogólną funkcję merge_with, która pobiera różną liczbę argumentów słownikowych i radzi sobie ze zduplikowanymi kluczami z dostarczoną funkcją. Gdy skończę, opublikuję to w innym wątku, w którym rozwiązanie jest bardziej odpowiednie.
Lei Zhao

Oto link, w którym napisałem bardziej ogólne rozwiązanie. Witamy i zobacz.
Lei Zhao,


1

Rozpoczynając od Python 3.9, operator |tworzy nowy słownik ze scalonymi kluczami i wartościami z dwóch słowników:

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

To:

Tworzy nowy słownik d3 ze scalonymi kluczami i wartościami d2 i d1. Wartości d1 mają pierwszeństwo, gdy d2 i d1 mają wspólne klucze.


Zwróć także uwagę na |=operator, który modyfikuje d2 przez scalenie d1, z priorytetem na wartościach d1:

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


0

Uważam, że, jak wspomniano powyżej, używanie d2.update(d1)jest najlepszym podejściem i że możesz również skopiować d2najpierw, jeśli nadal tego potrzebujesz.

Chociaż chcę dict(d1, **d2)zauważyć, że jest to w rzeczywistości zły sposób łączenia słowników w ogóle, ponieważ argumenty słów kluczowych muszą być ciągami, więc nie powiedzie się, jeśli masz dicttaki jak:

{
  1: 'foo',
  2: 'bar'
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.