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.
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.
Odpowiedzi:
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.deepcopy
należy również wziąć pod uwagę.
isinstance(int, object) is True
ale deepcopy
nie wydaje się to konieczne.
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.
**
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.
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.
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.
.update
wywołaniem ( merged={}
po którym następuje for d in dict: merged.update(d)
) byłaby krótsza, bardziej czytelna i wydajniejsza.
reduce
i lambda
s, co powiesz na to return reduce(lambda x, y: x.update(y) or x, dicts, {})
?
Trey Hunner ma fajny wpis na blogu przedstawiający kilka opcji łączenia wielu słowników, w tym (dla python3.3 +) ChainMap i rozpakowywanie słownika .
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}
Uważam, że, jak wspomniano powyżej, używanie d2.update(d1)
jest najlepszym podejściem i że możesz również skopiować d2
najpierw, 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 dict
taki jak:
{
1: 'foo',
2: 'bar'
}
**
przekazywania argumentów słów kluczowych, chyba że wszystkie kluczed2
są ciągami. Jeśli nie wszystkie kluczed2
są 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 .