аватар on5fydu8@gmail.com · 01.01.1970 03:00

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

 

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

Если вам не нравится +, можно же было сделать fluent interface - чтобы dict.update() возвращал self.

Самое лучшее, что я нашел - dict(dict1, key1=value1).

Пример использования:

base_config = {'key1': 'val1'}func1(base_config + {'key2': 'val2'})func2(base_config + {'key3': 'val3'})

 

аватар on5fydu8@gmail.com · 01.01.1970 03:00

Update: В Python 3.9 реализовано PEP 584 -- Add Union Operators To dict :

n
>>> {0: 'a', 1: 'b'} | {False: 'A', 2: 'C'}n{0: 'A', 1: 'b', 2: 'C'}n
n

объединение словарей с помощью | (union) операции: более левые ключи (как у | операции для set) выигрывают и более правые (поздние) значения выигрывают (как dict_add_keep_last() ниже).

n

Есть также и по месту операция (изменяет словарь) и можно передавать не только словари:

n
>>> d = {0: 'a', 1: 'b'}n>>> d |= [(False, 'A'), (2, 'C')]n>>> dn{0: 'A', 1: 'b', 2: 'C'}n
n
n

Cтарый ответ:

n

Потому что не ясно какую операцию + должен реализовывать для словарей. И предпочтительные для каждого конкретного случая варианты легко реализовать.

n

Из ""The Zen of Python"" (import this) :

n
Explicit is better than implicit.nIn the face of ambiguity, refuse the temptation to guess.nThere should be one-- and preferably only one --obvious way to do it.n
n

BDFL (2009) :

n
n

Because there so many different ways to think about this, it's better not to guess and force the user to be explicit.

n
n

Guido (2019): Why operators are useful .

n

Например (псевдо-код):

n
result = {'a': 1, 'b': 2} + {'a': 3, 'c': 0}n
n

Возможны разные ответы:

n
    n
  • n

    сохранять только последние значения ( PEP-0584 поведение )

    n
    result = {'a': 3, 'b': 2, 'c': 0}n
    n

    можно реализовать так:

    n
    def dict_add_keep_last(a, b): # aka merged() or updated()n    d = a.copy()n    d.update(b)n    retu dn
    n

    В специальных случаях, когда ключи являются строками можно без функции обойтись (в Питоне 2 (CPython) также разрешены произвольные ключи), пример :

    n
    result = dict(a, **b) # результат тот жеn
    n

    Guido не любит такую конструкцию .

    n

    Или для произвольных словарей в Питоне 3.5 ( PEP 0448 -- Additional Unpacking Generalizations ):

    n
    result = {**a, **b}n
    n

    см. Adding dictionaries together, Python (следуй по ссылкам по цепочке, есть хорошие ответы у каждого вопроса).

    n
  • n
  • n

    сохранять первые значения (возможно ближе к тому как | операция реализована у set()):

    n
    result = {**b, **a} # Python 3.5, or else swap a, b: dict_add_keep_last(b, a)n
    n
  • n
  • n

    суммировать значения как collections.Counter (Multiset semantics):

    n
    >>> from collections import Counte>>> Counter(a) + Counter(b)nCounter({'a': 4, 'b': 2})n
    n

    Ключи с нулевыми (и отрицательными) значениями ('c') не сохраняются.

    n
  • n
  • n

    как-то по-другому комбинировать, чтобы не терять информацию, например, sum_dict() или:

    n
    result = {'a': [1, 3], 'b': 2, 'c': 0}n
    n

    Use-case: повторяющиеся ключи в json объекте

    n
  • n
  • n

    завершаться с ошибкой в случае ключей-дубликатов, пример .

    n
  • n
n

Никакой из представленных вариантов не является более очевидным (точнее разные люди могут считать разные варианты более очевидными). Более того, каждый из этих вариантов имеет право на существование и они не всегда взаимозаменяемы. Различные варианты сложения словарей используются не очень часто, поэтому важно, чтобы в каждом случае явно было указано, какой тип операции используется и не нужно было гадать (ошибки менее вероятны).

n

Идея обсуждалась в списке рассылки (python-ideas) неоднократно:

n
    n
  • ""Adding ""+"" and ""+="" operators to dict"" (2015)
  • n
  • ""adding dictionaries"" (2014)
  • n
  • ""dict '+' operator and slicing support for pop"" (2009)
  • n
n

Мелкие замечания (не имеют отношения к ""почему"", но полезны для темы ""сложить два словаря""):

n
    n
  • n

    тип возвращаемого значения для a + b выражения, как обычно, не очевиден и может зависеть от типа a и b например, выражение a += b, может ""поменять"" тип a:

    n
    >>> from decimal import Decimaln>>> a = 1n>>> a += Decimal(2)n>>> anDecimal('3')n
    n
  • n
  • n

    также, в случае нескольких словарей, + синтаксис поощряет неэффективный (память, время исполнения) код для больших словарей, например, a += b + c + d + .. (O(N*k**2)) можно записать б��лее эффективно как (O(N*k)):

    n
    for it in [b, c, d, ..]:n    a.update(it)n
    n
  • n
  • n

    иногда (например, для больших словарей, в которых только некоторые ключи являются интересными) можно ""лениво"" (O(k)) слить словари, используя ChainMap:

    n
    try: n    from collections import ChainMapnexcept ImportError: # Python 2n    from ConfigParser import _Chainmap as ChainMapn
    n

    pylookup = ChainMap(locals(), globals(), vars(builtins))

    n
  • n
n
n
n

Если вам не нравится +, можно же было сделать fluent interface - чтобы dict.update() возвращал self.

n
n

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

n

Последние

Похожие