Як я можу об'єднати два словники 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