Почему нельзя просто взять и сложить два словаря?

1,00
р.
Почему списки, кортежи, множества можно, а словари - нельзя?
Если вам не нравится +, можно же было сделать fluent interface - чтобы dict.update() возвращал self.
Самое лучшее, что я нашел - dict(dict1, key1=value1).
Пример использования:
base_config = {'key1': 'val1'}
func1(base_config + {'key2': 'val2'}) func2(base_config + {'key3': 'val3'})

Ответ
Update: В Python 3.9 реализовано PEP 584 -- Add Union Operators To dict:
>>> {0: 'a', 1: 'b'} | {False: 'A', 2: 'C'} {0: 'A', 1: 'b', 2: 'C'}
объединение словарей с помощью | (union) операции: более левые ключи (как у | операции для set) выигрывают и более правые (поздние) значения выигрывают (как dict_add_keep_last() ниже).
Есть также и по месту операция (изменяет словарь) и можно передавать не только словари:
>>> d = {0: 'a', 1: 'b'} >>> d |= [(False, 'A'), (2, 'C')] >>> d {0: 'A', 1: 'b', 2: 'C'}

Cтарый ответ:
Потому что не ясно какую операцию + должен реализовывать для словарей. И предпочтительные для каждого конкретного случая варианты легко реализовать.
Из "The Zen of Python" (import this):
Explicit is better than implicit. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it.
BDFL (2009):
Because there so many different ways to think about this, it's better not to guess and force the user to be explicit.
Guido (2019): Why operators are useful.
Например (псевдо-код):
result = {'a': 1, 'b': 2} + {'a': 3, 'c': 0}
Возможны разные ответы:
сохранять только последние значения (PEP-0584 поведение) result = {'a': 3, 'b': 2, 'c': 0}
можно реализовать так: def dict_add_keep_last(a, b): # aka merged() or updated() d = a.copy() d.update(b) return d
В специальных случаях, когда ключи являются строками можно без функции обойтись (в Питоне 2 (CPython) также разрешены произвольные ключи), пример: result = dict(a, **b) # результат тот же
Guido не любит такую конструкцию. Или для произвольных словарей в Питоне 3.5 (PEP 0448 -- Additional Unpacking Generalizations): result = {**a, **b}
см. Adding dictionaries together, Python (следуй по ссылкам по цепочке, есть хорошие ответы у каждого вопроса). сохранять первые значения (возможно ближе к тому как | операция реализована у set()): result = {**b, **a} # Python 3.5, or else swap a, b: dict_add_keep_last(b, a)
суммировать значения как collections.Counter (Multiset semantics): >>> from collections import Counter >>> Counter(a) + Counter(b) Counter({'a': 4, 'b': 2})
Ключи с нулевыми (и отрицательными) значениями ('c') не сохраняются. как-то по-другому комбинировать, чтобы не терять информацию, например, sum_dict() или: result = {'a': [1, 3], 'b': 2, 'c': 0}
Use-case: повторяющиеся ключи в json объекте завершаться с ошибкой в случае ключей-дубликатов, пример.
Никакой из представленных вариантов не является более очевидным (точнее разные люди могут считать разные варианты более очевидными). Более того, каждый из этих вариантов имеет право на существование и они не всегда взаимозаменяемы. Различные варианты сложения словарей используются не очень часто, поэтому важно, чтобы в каждом случае явно было указано, какой тип операции используется и не нужно было гадать (ошибки менее вероятны).
Идея обсуждалась в списке рассылки (python-ideas) неоднократно:
"Adding "+" and "+=" operators to dict" (2015) "adding dictionaries" (2014) "dict '+' operator and slicing support for pop" (2009)
Мелкие замечания (не имеют отношения к "почему", но полезны для темы "сложить два словаря"):
тип возвращаемого значения для a + b выражения, как обычно, не очевиден и может зависеть от типа a и b например, выражение a += b, может "поменять" тип a: >>> from decimal import Decimal >>> a = 1 >>> a += Decimal(2) >>> a Decimal('3')
также, в случае нескольких словарей, + синтаксис поощряет неэффективный (память, время исполнения) код для больших словарей, например, a += b + c + d + .. (O(N*k**2)) можно записать более эффективно как (O(N*k)): for it in [b, c, d, ..]: a.update(it)
иногда (например, для больших словарей, в которых только некоторые ключи являются интересными) можно "лениво" (O(k)) слить словари, используя ChainMap: try: from collections import ChainMap except ImportError: # Python 2 from ConfigParser import _Chainmap as ChainMap
pylookup = ChainMap(locals(), globals(), vars(builtins))

Если вам не нравится +, можно же было сделать fluent interface - чтобы dict.update() возвращал self.
Потому что в Питоне методы, которые изменяют объект, обычно возвращают None, типичный пример: some_list.sort() vs. sorted(some_list). Питон следует принципу Command–query separation.