Як об’єднати словники разом у Python?


91
d3 = dict(d1, **d2)

Я розумію, що це об’єднує словник. Але чи це унікально? Що робити, якщо d1 має той самий ключ, що і d2, але різне значення? Я хотів би, щоб d1 і d2 були об’єднані, але d1 має пріоритет, якщо є дублікат ключа.


9
Пам'ятайте, що цей фокус вважається зловживанням **передачею аргументу ключового слова, якщо всі ключі не d2є рядками. Якщо не всі ключі d2є рядками, це не вдається в Python 3.2 та в альтернативних реалізаціях Python, таких як Jython, IronPython та PyPy. Див., Наприклад, mail.python.org/pipermail/python-dev/2010-April/099459.html .
Марк Дікінсон

Відповіді:


154

Ви можете використовувати .update()метод, якщо оригінал вам більше не потрібен d2:

Оновіть словник парами ключ / значення з інших, перезаписавши існуючі ключі . Повернення None.

Наприклад:

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

Оновлення:

Звичайно, ви можете скопіювати словник спочатку, щоб створити новий об’єднаний. Це може бути або не потрібно. Якщо у вашому словнику є складні об’єкти (об’єкти, що містять інші об’єкти, такі як списки чи екземпляри класів), copy.deepcopyце також слід врахувати.


1
У цьому випадку елементи d1 повинні правильно отримувати пріоритет, якщо виявляються суперечливі ключі
Trey Hunner

Якщо вам це все ще потрібно, просто зробіть копію. d3 = d2.copy () d3.update (d1), але я хотів би, щоб d1 + d2 додавались до мови.
стех

4
d1 + d2 проблематично, оскільки один словник повинен мати пріоритет під час конфліктів, і не особливо очевидно, який саме.
rjh

d1 + d2 буде реалізовано лише в тому випадку, якщо Python отримає мультимапу, інакше неоднозначність для користувача занадто заплутана для 8-байтного набору тексту.
Нік Бастін,

У цьому прикладі у словнику є об’єкти: isinstance(int, object) is Trueпроте deepcopyце не здається необхідним.
Antony Hatchkins

43

У Python2,

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

d1 замінює d2:

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

d2 замінює d1:

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

Така поведінка є не просто випадковим впровадженням; це гарантовано в документації :

Якщо ключ вказаний як у позиційному аргументі, так і як аргумент ключового слова, значення, пов'язане з ключовим словом, зберігається у словнику.


3
Ваші приклади зазнають невдачі (створюючи помилку TypeError) у Python 3.2, а в поточних версіях Jython, PyPy та IronPython: для цих версій Python при передачі dict з **позначеннями всі ключі цього dict повинні бути рядками. Докладніше див. У потоці python-dev, починаючи з mail.python.org/pipermail/python-dev/2010-April/099427.html .
Марк Дікінсон

@Mark: Дякую за увагу. Я відредагував код, щоб зробити його сумісним із реалізаціями, не пов'язаними з CPython.
unutbu

3
це не вдається, якщо ваші ключі являють собою кортежі рядків і чисел. наприклад, d1 = {(1, 'a'): 1, (1, 'b'): 0,} d2 = {(1, 'a'): 1, (2, 'b'): 2, (2, 'a'): 1,}
MySchizoBuddy

Щодо синтаксису розпакування, див. Цю публікацію щодо змін, які мають відбутися в python 3.5.
Іоанніс Філіппідіс,

Я збирався сказати, що це d = dict(**d1, **d2)працює, але саме на це посилається @IoannisFilippidis у своєму коментарі. Можливо, включення сюжетного фрагменту було б зрозумілішим, тому ось воно.
dwanderson

14

Якщо ви хочете d1мати пріоритет у конфліктах, зробіть:

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

В іншому випадку зворотне d2і d1.


1

Моє рішення полягає у визначенні функції злиття . Це не вишукано і коштує лише однієї лінії. Ось код у 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()) }

Тести

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

Це працює для довільної кількості аргументів словника. Якщо в цих словниках були повторювані ключі, виграє ключ від самого праворучного словника у списку аргументів.


1
Простий цикл із .updateвикликом у ньому ( merged={}за яким слід for d in dict: merged.update(d)) буде коротшим, читабельнішим та ефективнішим.
Марк Дікінсон,

1
Або якщо ви дійсно хочете використовувати reduceі lambdas, як щодо return reduce(lambda x, y: x.update(y) or x, dicts, {})?
Марк Дікінсон

1
Ви можете спробувати свій код в оболонці і перевірити, чи він правильний. Я намагався написати функцію, яка може приймати різну кількість аргументів словника з однаковою функціональністю. Краще не використовувати x.update (y) під лямбда, оскільки він завжди повертає None . І я намагаюся написати більш загальну функцію merge_with, яка приймає різну кількість аргументів словника та має справу з дублікатами ключів із наданою функцією. Закінчивши, я опублікую його в іншому потоці, де рішення є більш актуальним.
Лей Чжао

Ось посилання, де я написав загальніше рішення. Ласкаво просимо і подивимось.
Лей Чжао


1

Починаючи з Python 3.9, оператор |створює новий словник із об’єднаними ключами та значеннями з двох словників:

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

Це:

Створює новий словник d3 із об’єднаними ключами та значеннями d2 та d1. Значення d1 мають пріоритет, коли d2 і d1 діляться ключами.


Також зверніть увагу на |=оператор, який модифікує d2 шляхом злиття d1 у, з пріоритетом на значеннях d1:

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


0

Я вважаю, що, як зазначено вище, використання d2.update(d1)є найкращим підходом, і ви також можете скопіювати d2спочатку, якщо вам це все ще потрібно.

Хоча, я хочу зазначити, що dict(d1, **d2)насправді це поганий спосіб злиття словників загалом, оскільки аргументи ключових слів повинні бути рядками, отже, це не вдасться, якщо у вас є dictтакі, як:

{
  1: 'foo',
  2: 'bar'
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.