Dict слиться в понимании dict
В Python 3.5 мы можем объединять сообщения с помощью распаковки с двойными сплатами.
>>> d1 = {1: 'one', 2: 'two'}
>>> d2 = {3: 'three'}
>>> {**d1, **d2}
{1: 'one', 2: 'two', 3: 'three'}
Здорово. Похоже, это не обобщает динамические сценарии использования:
>>> ds = [d1, d2]
>>> {**d for d in ds}
SyntaxError: dict unpacking cannot be used in dict comprehension
Вместо этого мы должны сделать reduce(lambda x,y: {**x, **y}, ds, {})
, что кажется намного уродливее. Почему "один очевидный способ сделать это" не допускается синтаксическим анализатором, когда в этом выражении нет никакой двусмысленности?
7 ответов
Это не совсем ответ на ваш вопрос, но я хотел бы рассмотреть возможность использования ChainMap
быть идиоматичным и элегантным способом сделать то, что вы предлагаете (слияние словарей в линию):
>>> d1 = {1: 'one', 2: 'two'}
>>> d2 = {3: 'three'}
>>> ds = [d1, d2]
>>> dict(ChainMap(*ds))
{1: 'one', 2: 'two', 3: 'three'}
Хотя это не особенно прозрачное решение, так как многие программисты могут не знать точно, как ChainMap
работает. Обратите внимание, что (как указывает @AnttiHaapala) "используется первый найденный", поэтому, в зависимости от ваших намерений, вам может потребоваться позвонить reversed
перед прохождением вашего dict
с в ChainMap
,
>>> d2 = {3: 'three', 2:'LOL'}
>>> dict(ChainMap(*ds))
{1: 'one', 2: 'two', 3: 'three'}
>>> dict(ChainMap(*reversed(ds)))
{1: 'one', 2: 'LOL', 3: 'three'}
Для меня очевидным способом является:
d_out = {}
for d in ds:
d_out.update(d)
Это быстро и, вероятно, довольно эффективно. Я не знаю, что я могу говорить за разработчиков Python, но я не знаю, что ваша ожидаемая версия легче читается. Например, ваше понимание для меня больше похоже на комплексное понимание из-за отсутствия :
, FWIW, я не думаю, что есть какая-либо техническая причина (например, двусмысленность синтаксического анализатора), что они не могли добавить эту форму распаковки понимания.
По-видимому, эти формы были предложены, но не имели достаточно универсальной поддержки, чтобы оправдать их реализацию (пока).
идиоматический, без ChainMap
:
>>> d1 = {1: 'one', 2: 'two'}
>>> d2 = {3: 'three'}
>>> {k: v for d in [d1, d2] for k, v in d.items()}
{1: 'one', 2: 'two', 3: 'three'}
Вы могли бы использовать
itertools.chain
или же
itertools.chain.from_iterable
:
import itertools
ds = [{'a': 1, 'b': 2}, {'c': 30, 'b': 40}]
merged_d = dict(itertools.chain(*(d.items() for d in ds)))
print(merged_d) # {'a': 1, 'b': 40, 'c': 30}
Основано на этом решении, также упомянутом @ilgia-everilä, но делает его совместимым с Py2 и по-прежнему избегает промежуточных структур. Инкапсуляция его внутри функции делает его использование вполне читабельным.
def merge_dicts(*dicts, **extra):
"""
>>> merge_dicts(dict(a=1, b=1), dict(b=2, c=2), dict(c=3, d=3), d=4, e=4)
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 4}
"""
return dict((
(k,v)
for d in dicts
for k,v in d.items()
), **extra)
Вы можете определить эту функцию:
from collections import ChainMap
def mergeDicts(l):
return dict(ChainMap(*reversed(list(l))))
Затем вы можете использовать его так:
>>> d1 = {1: 'one', 2: 'two'}
>>> d2 = {3: 'three'}
>>> ds = [d1, d2]
>>> mergeDicts(ds)
{1: 'one', 2: 'two', 3: 'three'}