Як я можу об'єднати два словники Python в один вираз?
Для словників x
і y
, z
перетворюється на дрібно злитий словник зі значеннями y
заміни цих на x
.
У Python 3.5 або новішої версії:
z = {**x, **y}
У Python 2 (або 3.4 або нижчий) запишіть функцію:
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
і зараз:
z = merge_two_dicts(x, y)
У Python 3.9.0a4 або вище (остаточна дата випуску приблизно у жовтні 2020 р.): PEP-584 , обговорюваний тут , було втілено для подальшого спрощення цього:
z = x | y # NOTE: 3.9+ ONLY
Пояснення
Скажіть, у вас є два дикти, і ви хочете об'єднати їх у новий дикт, не змінюючи початкові дикти:
x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
Бажаним результатом є отримання нового словника ( z
) зі значеннями об'єднаних значень, а значення другого диктату замінюють значення перших.
>>> z
{'a': 1, 'b': 3, 'c': 4}
Новий синтаксис для цього, запропонований у PEP 448 та доступний як у Python 3.5 , є
z = {**x, **y}
І це справді єдиний вираз.
Зауважте, що ми можемо також об'єднатися з буквальними позначеннями:
z = {**x, 'foo': 1, 'bar': 2, **y}
і зараз:
>>> z
{'a': 1, 'b': 3, 'foo': 1, 'bar': 2, 'c': 4}
Зараз він показує, як реалізовано в графіку випуску для 3.5, PEP 478 , і тепер пробився в документ « Що нового в Python 3.5» .
Однак, оскільки багато організацій продовжують працювати на Python 2, можливо, ви хочете зробити це сумісно назад. Класично пітонічний спосіб, доступний в Python 2 та Python 3.0-3.4, полягає в тому, щоб зробити це як двоетапний процес:
z = x.copy()
z.update(y) # which returns None since it mutates z
В обох підходах y
вийде друге місце, і його значення замінять x
значення, тим самим 'b'
вкажуть на 3
наш кінцевий результат.
Ще не на Python 3.5, але хочеться одного виразу
Якщо ви ще не користуєтесь Python 3.5 або вам потрібно написати код, сумісний із зворотним ходом, і вам потрібно це в одному виразі , найефективнішим при правильному підході є введення його у функцію:
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
і тоді у вас є один вираз:
z = merge_two_dicts(x, y)
Ви також можете створити функцію для об'єднання невизначеної кількості диктів, від нуля до дуже великого числа:
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
Ця функція буде працювати в Python 2 і 3 для всіх диктів. наприклад , дані dicts a
в g
:
z = merge_dicts(a, b, c, d, e, f, g)
і ключові пари значень в g
матиме пріоритет над dicts a
до f
, і так далі.
Критика інших відповідей
Не використовуйте те, що бачите у раніше прийнятій відповіді:
z = dict(x.items() + y.items())
У Python 2 ви створюєте два списки в пам'яті для кожного диктату, створюєте третій список у пам'яті довжиною, що дорівнює довжині перших двох разом, а потім відкидаєте всі три списки для створення диктату. У Python 3 це не вдасться, оскільки ви додаєте два dict_items
об'єкти разом, а не два списки -
>>> 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'
і вам доведеться явно створити їх як списки, наприклад z = dict(list(x.items()) + list(y.items()))
. Це марнотрата ресурсів та обчислювальної потужності.
Аналогічно, прийняття об'єднання items()
в Python 3 ( viewitems()
у Python 2.7) також буде невдалим, коли значення є нерозбірливими об'єктами (наприклад, списки, наприклад). Навіть якщо ваші значення є доступними, оскільки множини семантично не упорядковані, поведінка не визначена щодо переваги. Тому не робіть цього:
>>> c = dict(a.items() | b.items())
Цей приклад демонструє, що відбувається, коли значення не змінні:
>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Ось приклад, де y повинен мати перевагу, але натомість значення з x зберігається завдяки довільному порядку множин:
>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}
Ще один хак, який ви не повинні використовувати:
z = dict(x, **y)
Для цього використовується dict
конструктор, і він дуже швидкий і ефективний для пам'яті (навіть трохи більше, ніж наш двоетапний процес), але якщо ви точно не знаєте, що відбувається тут (тобто другий дікт передається як аргументи ключових слів в дікт конструктор), це важко читати, це не призначене використання, а значить, не піфонічне.
Ось приклад використання, яке відновлюють у джанго .
Дикти призначені для прийняття хешируемих ключів (наприклад, frozensets або кортежів), але цей спосіб виходить з ладу в Python 3, коли ключі не є рядками.
>>> c = dict(a, **b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings
Із списку розсилки Гідо ван Россум, творець мови, написав:
Мені добре, що оголосив наказ ({}, ** {1: 3}) незаконним, оскільки врешті-решт це зловживання механізмом **.
і
Мабуть, dict (x, ** y) відбувається навколо "cool hack" для "call x.update (y) та return x". Особисто я вважаю це більш зневажливим, ніж крутим.
Це моє розуміння (як і розуміння творця мови ), що призначене для використання призначене dict(**y)
для створення диктовок з метою читабельності, наприклад:
dict(a=1, b=10, c=11)
замість
{'a': 1, 'b': 10, 'c': 11}
Відповідь на коментарі
Незважаючи на те, що говорить Гвідо, dict(x, **y)
відповідає специфікації диктату, яка не виникла. працює як для Python 2, так і 3. Факт, що це працює лише для рядкових клавіш, є прямим наслідком того, як працюють параметри ключових слів, а не короткий час дікта. Також використання оператора ** в цьому місці не зловживає механізмом, насправді ** було розроблено саме для передачі диктовок як ключових слів.
Знову ж, це не працює для 3, коли клавіші не є рядками. Договір неявного виклику полягає в тому, що простори імен приймають звичайні дикти, тоді як користувачі повинні передавати лише аргументи ключових слів, які є рядками. Усі інші дзвінки переслідували це. dict
порушив цю послідовність у 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}
Ця невідповідність була поганою, враховуючи інші реалізації Python (Pypy, Jython, IronPython). Таким чином, це було зафіксовано в Python 3, оскільки це використання може стати суттєвою зміною.
Я стверджую, що це злісна некомпетентність навмисно писати код, який працює лише в одній версії мови або працює лише з певними довільними обмеженнями.
Більше коментарів:
dict(x.items() + y.items())
як і раніше найбільш читабельне рішення для Python 2. Читання читається.
Моя відповідь: merge_two_dicts(x, y)
насправді мені здається набагато зрозумілішим, якщо насправді нас турбує читабельність. І це не сумісно вперед, оскільки Python 2 все більше застаріває.
{**x, **y}
схоже, не обробляє вкладені словники. вміст вкладених ключів просто перезаписується, а не зливається [...] Мене в результаті спалили ці відповіді, які не зливаються рекурсивно, і я був здивований, що ніхто про це не згадував. У моєму тлумаченні слова "злиття" ці відповіді описують "оновлення одного диктату з іншим", а не злиття.
Так. Я мушу повернути вас до питання, яке вимагає неглибокого злиття двох словників, причому значення першого будуть переписані другими - в одному виразі.
Якщо припустити два словники словників, можна рекурсивно об'єднати їх в одну функцію, але слід бути обережним, щоб не змінювати дикти з будь-якого джерела, і найнадійніший спосіб уникнути цього - це зробити копію при призначенні значень. Оскільки ключі повинні бути хешированными і, як правило, незмінні, копіювати їх безглуздо:
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
Використання:
>>> x = {'a':{1:{}}, 'b': {2:{}}}
>>> y = {'b':{10:{}}, 'c': {11:{}}}
>>> dict_of_dicts_merge(x, y)
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}
Створення непередбачуваних випадків для інших типів значень далеко виходить за рамки цього питання, тому я накажу вам свою відповідь на канонічне запитання на тему "Злиття словників" .
Менш ефективні, але правильні спеціальні
Ці підходи менш ефективні, але вони забезпечать правильну поведінку. Вони будуть набагато менш продуктивним , ніж copy
та update
чи новий розпакування , тому що вони перебирати кожної пари ключ-значення на більш високому рівні абстракції, але вони роблять дотримуватися порядку старшинства (останні dicts мають перевагу)
Ви також можете зв'язати дикти вручну всередині розуміння дикта:
{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7
або в python 2.6 (і, можливо, вже в 2.4, коли вводилися вирази генератора):
dict((k, v) for d in dicts for k, v in d.items())
itertools.chain
буде ланцюжок ітераторів над парами ключ-значення у правильному порядку:
import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))
Аналіз ефективності
Я збираюся лише зробити аналіз продуктивності, за якими відомо, що вони поводяться правильно.
import timeit
На Ubuntu 14.04 робиться наступне
У Python 2.7 (система 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
У Python 3.5 (глухонімих 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
Ресурси зі словників
z = x | y