Страницы

Поиск по вопросам

пятница, 29 ноября 2019 г.

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

#python #типы_данных #language_lawyer


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

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

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

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

base_config = {'key1': 'val1'}

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

    


Ответы

Ответ 1



Потому что не ясно какую операцию + должен реализовывать для словарей. И предпочтительные для каждого конкретного случая варианты легко реализовать. Из "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.

Ответ 2



Потому что в словаре есть ключи, которые могут совпадать в двух разных словарях.

Ответ 3



Операция сложения для словарей в Python не определена. При острой необходимости имитировать такое поведение: import itertools base_config = {'key1': 'val1'} func1(dict(itertools.chain(base_config.iteritems(), {'key2': 'val2'}.iteritems()))) func2(dict(itertools.chain(base_config.iteritems(), {'key3': 'val3'}.iteritems()))) При одинаковых ключах приоритет отдается последнему вхождению. Для Python3 .iteritems(...) заменить на .items(...)

Комментариев нет:

Отправить комментарий