У меня есть два словаря Python, и я хочу написать одно выражение, которое возвращает эти два словаря, объединенные. Метод update() был бы тем, что мне нужно, если бы он возвращал свой результат вместо изменения словаря на месте.

>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> z = x.update(y)
>>> print(z)
None
>>> x
{'a': 1, 'b': 10, 'c': 11}

Как я могу получить этот окончательный объединенный словарь в z, а не x?

(Для большей ясности, обработка конфликтов dict.update() в последнюю очередь выигрывает также.)

6067
Carl Meyer 2 Сен 2008 в 11:44

30 ответов

Если вы думаете, что лямбды - это зло, не читайте дальше. По запросу вы можете написать быстрое и эффективное для памяти решение с одним выражением:

x = {'a':1, 'b':2}
y = {'b':10, 'c':11}
z = (lambda a, b: (lambda a_copy: a_copy.update(b) or a_copy)(a.copy()))(x, y)
print z
{'a': 1, 'c': 11, 'b': 10}
print x
{'a': 1, 'b': 2}

Как предложено выше, использование двух строк или написание функции, вероятно, является лучшим способом.

37
EMS 23 Ноя 2011 в 18:20

Я знаю, что это не совсем соответствует специфике вопросов («одна строка»), но, поскольку ни один из приведенных выше ответов не пошел в этом направлении, в то время как множество и много ответов касались проблемы производительности, я чувствовал, что Я должен поделиться своими мыслями.

В зависимости от варианта использования может не потребоваться создание «реального» объединенного словаря из заданных входных словарей. представление , которое делает это, может быть достаточным во многих случаях, т.е. е . объект, который действует как в объединенном словаре, не вычисляя его полностью. Ленивая версия объединенного словаря, так сказать.

В Python это довольно просто и может быть сделано с помощью кода, приведенного в конце моего поста. Учитывая это, ответ на оригинальный вопрос будет:

z = MergeDict(x, y)

При использовании этого нового объекта он будет вести себя как объединенный словарь, но у него будет постоянное время создания и постоянный объем памяти, а исходные словари останутся нетронутыми. Создание его намного дешевле, чем в других предлагаемых решениях.

Конечно, если вы будете часто использовать результат, то в какой-то момент вы достигнете предела, когда создание реального объединенного словаря было бы более быстрым решением. Как я уже сказал, это зависит от вашего варианта использования.

Если вы когда-либо думали, что вы бы предпочли иметь реальное слияние dict, то вызов dict(z) произведет его (но, конечно, намного дороже, чем другие решения, поэтому об этом просто стоит упомянуть).

Вы также можете использовать этот класс для создания своего рода словаря для копирования при записи:

a = { 'x': 3, 'y': 4 }
b = MergeDict(a)  # we merge just one dict
b['x'] = 5
print b  # will print {'x': 5, 'y': 4}
print a  # will print {'y': 4, 'x': 3}

Вот простой код MergeDict:

class MergeDict(object):
  def __init__(self, *originals):
    self.originals = ({},) + originals[::-1]  # reversed

  def __getitem__(self, key):
    for original in self.originals:
      try:
        return original[key]
      except KeyError:
        pass
    raise KeyError(key)

  def __setitem__(self, key, value):
    self.originals[0][key] = value

  def __iter__(self):
    return iter(self.keys())

  def __repr__(self):
    return '%s(%s)' % (
      self.__class__.__name__,
      ', '.join(repr(original)
          for original in reversed(self.originals)))

  def __str__(self):
    return '{%s}' % ', '.join(
        '%r: %r' % i for i in self.iteritems())

  def iteritems(self):
    found = set()
    for original in self.originals:
      for k, v in original.iteritems():
        if k not in found:
          yield k, v
          found.add(k)

  def items(self):
    return list(self.iteritems())

  def keys(self):
    return list(k for k, _ in self.iteritems())

  def values(self):
    return list(v for _, v in self.iteritems())
6
Alfe 18 Май 2016 в 15:57
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

Для элементов с ключами в обоих словарях ('b') вы можете контролировать, какой из них окажется в выводе, поместив этот последний.

58
Greg Hewgill 2 Сен 2008 в 07:49

Используя понимание, вы можете

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}

dc = {xi:(x[xi] if xi not in list(y.keys()) 
           else y[xi]) for xi in list(x.keys())+(list(y.keys()))}

Дает

>>> dc
{'a': 1, 'c': 11, 'b': 10}

Обратите внимание на синтаксис if else в понимании

{ (some_key if condition else default_key):(something_if_true if condition 
          else something_if_false) for key, value in dict_.items() }
5
kiriloff 27 Май 2013 в 09:04

В python3 метод items больше не возвращает список, а скорее представление , которое действует как набор. В этом случае вам нужно взять объединение множеств, так как конкатенация с + не будет работать:

dict(x.items() | y.items())

Для python3-подобного поведения в версии 2.7 метод viewitems должен работать вместо items:

dict(x.viewitems() | y.viewitems())

В любом случае, я предпочитаю эту запись, так как кажется более естественным думать о ней как о операции объединения множеств, а не о конкатенации (как видно из заголовка).

< Сильный > Edit:

Еще пара моментов для Python 3. Во-первых, обратите внимание, что трюк dict(x, **y) не будет работать в Python 3, если ключи в y не являются строками.

Кроме того, Chainmap Рэймонда Хеттингера answer довольно элегантен, поскольку может принимать в качестве аргументов произвольное количество диктов, но из документов похоже, что он последовательно просматривает список всех диктовок для каждого поиска:

Поиски последовательно выполняют поиск соответствующих отображений, пока не будет найден ключ.

Это может замедлить вас, если в вашем приложении много поисков:

In [1]: from collections import ChainMap
In [2]: from string import ascii_uppercase as up, ascii_lowercase as lo; x = dict(zip(lo, up)); y = dict(zip(up, lo))
In [3]: chainmap_dict = ChainMap(y, x)
In [4]: union_dict = dict(x.items() | y.items())
In [5]: timeit for k in union_dict: union_dict[k]
100000 loops, best of 3: 2.15 µs per loop
In [6]: timeit for k in chainmap_dict: chainmap_dict[k]
10000 loops, best of 3: 27.1 µs per loop

Так что примерно на порядок медленнее для поисков. Я фанат Chainmap, но выглядит менее практичным там, где может быть много поисков.

29
Community 23 Май 2017 в 12:34

Опираясь на идеи здесь и в других местах, я понял функцию:

def merge(*dicts, **kv): 
      return { k:v for d in list(dicts) + [kv] for k,v in d.items() }

Использование (протестировано в Python 3):

assert (merge({1:11,'a':'aaa'},{1:99, 'b':'bbb'},foo='bar')==\
    {1: 99, 'foo': 'bar', 'b': 'bbb', 'a': 'aaa'})

assert (merge(foo='bar')=={'foo': 'bar'})

assert (merge({1:11},{1:99},foo='bar',baz='quux')==\
    {1: 99, 'foo': 'bar', 'baz':'quux'})

assert (merge({1:11},{1:99})=={1: 99})

Вы можете использовать лямбду вместо этого.

10
Bijou Trouvaille 19 Июл 2013 в 05:49

Это так глупо, что .update ничего не возвращает.
Я просто использую простую вспомогательную функцию для решения проблемы:

def merge(dict1,*dicts):
    for dict2 in dicts:
        dict1.update(dict2)
    return dict1

Примеры:

merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy
7
GetFree 2 Мар 2014 в 01:44

Если вы не против мутировать x,

x.update(y) or x

Простой, читаемый, производительный. Вы знаете update() всегда возвращаете None, что является ложным значением. Поэтому приведенное выше выражение всегда будет иметь значение x после его обновления.

Методы мутации в стандартной библиотеке (например, .update()) возвращают None по соглашению, поэтому этот шаблон будет работать и с ними. Если вы используете метод, который не следует этому соглашению, то or может не работать. Но вы можете использовать отображение и индекс кортежа, чтобы сделать его одним выражением. Это работает независимо от того, что оценивает первый элемент.

(x.update(y), x)[-1]

Если у вас еще нет x в переменной, вы можете использовать lambda для создания локального объекта без использования оператора присваивания. Это равносильно использованию lambda в качестве let expression , что является распространенным приемом в функциональных языках, но, возможно, не пифоническим.

(lambda x: x.update(y) or x)({'a': 1, 'b': 2})

Хотя это не сильно отличается от следующего использования оператора new walrus (только Python 3.8+):

(x := {'a': 1, 'b': 2}).update(y) or x

Если вам нужна копия, стиль PEP 448 - самый простой {**x, **y}. Но если это недоступно в вашей (более старой) версии Python, шаблон let работает и здесь.

(lambda z: z.update(y) or z)(x.copy())

(Это, конечно, эквивалентно (z := x.copy()).update(y) or z, но если ваша версия Python достаточно новая для этого, тогда будет доступен стиль PEP 448.)

8
gilch 17 Янв 2020 в 06:00
def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)

Среди таких сомнительных и сомнительных ответов этот яркий пример является единственным и единственным хорошим способом объединения диктов в Python, одобренным диктатором на всю жизнь Гвидо ван Россумом ! Кто-то предложил половину этого, но не включил его в работу.

print dict_merge(
      {'color':'red', 'model':'Mini'},
      {'model':'Ferrari', 'owner':'Carl'})

Дает :

{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}
46
Sam Watkins 6 Авг 2012 в 09:30

Рекурсивно / глубокое обновление диктанта

def deepupdate(original, update):
    """
    Recursively update a dict.
    Subdict's won't be overwritten but also updated.
    """
    for key, value in original.iteritems(): 
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            deepupdate(value, update[key]) 
    return update

Демонстрация :

pluto_original = {
    'name': 'Pluto',
    'details': {
        'tail': True,
        'color': 'orange'
    }
}

pluto_update = {
    'name': 'Pluutoo',
    'details': {
        'color': 'blue'
    }
}

print deepupdate(pluto_original, pluto_update)

Выходы:

{
    'name': 'Pluutoo',
    'details': {
        'color': 'blue',
        'tail': True
    }
}

Спасибо Rednaw за правки.

84
Dawid Gosławski 18 Дек 2015 в 11:19

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

Примеры следуют:

a = { 'one': { 'depth_2': True }, 'two': True }
b = { 'one': { 'extra': False } }
print dict(a.items() + b.items())

Можно ожидать, что-то вроде этого:

{ 'one': { 'extra': False', 'depth_2': True }, 'two': True }

Вместо этого мы получаем это:

{'two': True, 'one': {'extra': False}}

Элемент 'one' должен был иметь значения 'deep_2' и 'extra' в качестве элементов внутри своего словаря, если это действительно слияние.

Использование цепочки также не работает:

from itertools import chain
print dict(chain(a.iteritems(), b.iteritems()))

Результаты в:

{'two': True, 'one': {'extra': False}}

Глубокое слияние, которое дал rcwesick, также создает тот же результат.

Да, это будет работать для объединения образцов словарей, но ни один из них не является универсальным механизмом объединения. Я обновлю это позже, когда напишу метод, который выполняет истинное слияние.

16
Thanh Lim 3 Авг 2012 в 23:36
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

Это должно решить вашу проблему.

10
reetesh11 30 Ноя 2015 в 13:04

В вашем случае вы можете сделать следующее:

z = dict(x.items() + y.items())

Это, как вы хотите, поместит окончательный dict в z и сделает значение ключа b надлежащим образом переопределенным значением второго (y):

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(x.items() + y.items())
>>> z
{'a': 1, 'c': 11, 'b': 10}

Если вы используете Python 3, это будет немного сложнее. Чтобы создать z:

>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}
1587
wim 29 Авг 2018 в 17:18

Злоупотребление, приводящее к решению с одним выражением для ответа Мэтью:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (lambda f=x.copy(): (f.update(y), f)[1])()
>>> z
{'a': 1, 'c': 11, 'b': 10}

Вы сказали, что хотите одно выражение, поэтому я злоупотребил lambda, чтобы связать имя, и кортежами, чтобы переопределить ограничение в одно выражение для лямбды. Не стесняйтесь съеживаться.

Конечно, вы также можете сделать это, если не хотите копировать это:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (x.update(y), x)[1]
>>> z
{'a': 1, 'b': 10, 'c': 11}
22
Community 23 Май 2017 в 12:34

(Только для Python2.7 *; для Python3 * существуют более простые решения.)

Если вы не против импортировать стандартный модуль библиотеки, вы можете сделать

from functools import reduce

def merge_dicts(*dicts):
    return reduce(lambda a, d: a.update(d) or a, dicts, {})

(Бит or a в lambda необходим, потому что dict.update всегда возвращает None в случае успеха.)

11
kjo 28 Мар 2016 в 13:13

Хотя на этот вопрос уже был дан ответ несколько раз, это простое решение проблемы еще не было перечислено.

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z4 = {}
z4.update(x)
z4.update(y)

Это так же быстро, как z0 и зло z2, упомянутое выше, но легко понять и изменить.

47
phobie 14 Окт 2011 в 16:12

Альтернатива:

z = x.copy()
z.update(y)
620
Matthew Schinckel 2 Сен 2008 в 13:00

В Python 3.0 и более поздних версиях вы можете использовать { {X0}}, который группирует несколько диктовок или других сопоставлений для создания одного обновляемого представления:

>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = ChainMap({}, y, x)
>>> for k, v in z.items():
        print(k, '-->', v)

a --> 1
b --> 10
c --> 11

Обновление для Python 3.5 и более поздних версий . Вы можете использовать PEP 448 расширенный словарь упаковки и распаковки. Это быстро и просто:

>>> x = {'a':1, 'b': 2}
>>> y = y = {'b':10, 'c': 11}
>>> {**x, **y}
{'a': 1, 'b': 10, 'c': 11}
112
Neuron 20 Ноя 2019 в 17:03

Два словаря

def union2(dict1, dict2):
    return dict(list(dict1.items()) + list(dict2.items()))

n словари

def union(*dicts):
    return dict(itertools.chain.from_iterable(dct.items() for dct in dicts))

sum имеет плохую производительность. См. https://mathieularose.com/how. -не к выравниваться - а- список - из - списков - в - питон /

21
Mathieu Larose 2 Окт 2016 в 18:16

Простое решение с использованием itertools, сохраняющее порядок (последние имеют приоритет)

import itertools as it
merge = lambda *args: dict(it.chain.from_iterable(it.imap(dict.iteritems, args)))

И это использование:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> merge(x, y)
{'a': 1, 'b': 10, 'c': 11}

>>> z = {'c': 3, 'd': 4}
>>> merge(x, y, z)
{'a': 1, 'b': 10, 'c': 3, 'd': 4}
21
reubano 6 Сен 2016 в 11:30

Это, вероятно, не будет популярным ответом, но вы почти наверняка не хотите этого делать. Если вам нужна копия, которая является слиянием, используйте копию (или deepcopy, в зависимости от на что хочешь) а потом обновляйся. Две строки кода гораздо более читабельны - более Pythonic - чем создание одной строки с помощью .items () + .items (). Явное лучше, чем неявное.

Кроме того, когда вы используете .items () (до Python 3.0), вы создаете новый список, содержащий элементы из dict. Если ваши словари большие, то это довольно много накладных расходов (два больших списка, которые будут выброшены, как только будет создан объединенный диктат). update () может работать более эффективно, потому что он может проходить через второй элемент dict элемент за элементом.

С точки зрения времени:

>>> timeit.Timer("dict(x, **y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.52571702003479
>>> timeit.Timer("temp = x.copy()\ntemp.update(y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.694622993469238
>>> timeit.Timer("dict(x.items() + y.items())", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
41.484580039978027

ИМО крошечное замедление между первыми двумя стоит его для удобочитаемости. Кроме того, ключевые аргументы для создания словаря были добавлены только в Python 2.3, тогда как copy () и update () будут работать в более старых версиях.

207
twasbrillig 5 Авг 2014 в 23:56

Проблема с решениями, перечисленными на сегодняшний день, заключается в том, что в объединенном словаре значение ключа "b" равно 10, но, по моему мнению, оно должно быть 12. В этом свете я представляю следующее:

import timeit

n=100000
su = """
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
"""

def timeMerge(f,su,niter):
    print "{:4f} sec for: {:30s}".format(timeit.Timer(f,setup=su).timeit(n),f)

timeMerge("dict(x, **y)",su,n)
timeMerge("x.update(y)",su,n)
timeMerge("dict(x.items() + y.items())",su,n)
timeMerge("for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k] ",su,n)

#confirm for loop adds b entries together
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]
print "confirm b elements are added:",x

Результаты:

0.049465 sec for: dict(x, **y)
0.033729 sec for: x.update(y)                   
0.150380 sec for: dict(x.items() + y.items())   
0.083120 sec for: for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]

confirm b elements are added: {'a': 1, 'c': 11, 'b': 12}
10
upandacross 3 Дек 2013 в 18:11

Python 3.5 (PEP 448) допускает более приятную синтаксическую опцию:

x = {'a': 1, 'b': 1}
y = {'a': 2, 'c': 2}
final = {**x, **y} 
final
# {'a': 2, 'b': 1, 'c': 2}

Или даже

final = {'a': 1, 'b': 1, **x, **y}
64
Bilal Syed Hussain 26 Фев 2015 в 21:27

Я хотел что-то похожее, но с возможностью указать, как значения на дубликатах ключей были объединены, так что я взломал это (но не сильно протестировал это). Очевидно, что это не одно выражение, но это единственный вызов функции.

def merge(d1, d2, merge_fn=lambda x,y:y):
    """
    Merges two dictionaries, non-destructively, combining 
    values on duplicate keys as defined by the optional merge
    function.  The default behavior replaces the values in d1
    with corresponding values in d2.  (There is no other generally
    applicable merge strategy, but often you'll have homogeneous 
    types in your dicts, so specifying a merge technique can be 
    valuable.)

    Examples:

    >>> d1
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1)
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1, lambda x,y: x+y)
    {'a': 2, 'c': 6, 'b': 4}

    """
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge_fn(result[k], v)
        else:
            result[k] = v
    return result
110
Rainy 13 Сен 2014 в 19:56

Это можно сделать с помощью единого понимания:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> { key: y[key] if key in y else x[key]
      for key in set(x) + set(y)
    }

На мой взгляд, лучший ответ для части с «одним выражением», так как никаких дополнительных функций не требуется, и это короткий.

9
RemcoGerlich 17 Июл 2015 в 14:47

Лучшая версия, которую я мог бы подумать, не используя копию:

from itertools import chain
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
dict(chain(x.iteritems(), y.iteritems()))

Это быстрее, чем dict(x.items() + y.items()), но не так быстро, как n = copy(a); n.update(b), по крайней мере, на CPython. Эта версия также работает в Python 3, если вы измените iteritems() на items(), что автоматически выполняется инструментом 2to3.

Лично мне эта версия нравится больше всего, потому что она достаточно хорошо описывает то, что я хочу, в едином функциональном синтаксисе. Единственная незначительная проблема заключается в том, что не очевидно, что значения от y имеют приоритет над значениями от x, но я не верю, что это трудно понять.

69
driax 14 Окт 2010 в 18:55

Будьте питоничны. Используйте понимание:

z={i:d[i] for d in [x,y] for i in d}

>>> print z
{'a': 1, 'c': 11, 'b': 10}
32
Robino 29 Сен 2016 в 10:45
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> x, z = dict(x), x.update(y) or x
>>> x
{'a': 1, 'b': 2}
>>> y
{'c': 11, 'b': 10}
>>> z
{'a': 1, 'c': 11, 'b': 10}
8
John La Rooy 13 Ноя 2013 в 10:01

Другой, более краткий вариант:

z = dict(x, **y)

Примечание : это стало популярным ответом, но важно отметить, что если y имеет какие-либо нестроковые ключи, то, что это работает вообще, является злоупотреблением CPython детали реализации, и он не работает в Python 3, или в PyPy, IronPython или Jython. Кроме того, Гвидо не фанат. Поэтому я не могу рекомендовать эту технику для совместимого с прямым переносом кода или переносимого кода с перекрестной реализацией, что действительно означает, что его следует полностью избегать

331
Carl Meyer 21 Янв 2016 в 06:43

В последующем ответе вы спросили об относительной эффективности этих двух альтернатив:

z1 = dict(x.items() + y.items())
z2 = dict(x, **y)

По крайней мере, на моей машине (довольно обычный x86_64 с Python 2.5.2) альтернатива z2 не только короче и проще, но и значительно быстрее. Вы можете убедиться в этом сами, используя модуль timeit, который поставляется с Python.

Пример 1: идентичные словари, отображающие 20 последовательных целых чисел на себя:

% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z1=dict(x.items() + y.items())'
100000 loops, best of 3: 5.67 usec per loop
% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z2=dict(x, **y)' 
100000 loops, best of 3: 1.53 usec per loop

z2 выигрывает с коэффициентом 3,5 или около того. Кажется, что разные словари дают совершенно разные результаты, но z2 всегда кажется впереди. (Если вы получили противоречивые результаты для теста же , попробуйте передать -r с числом, превышающим значение по умолчанию 3.)

Пример 2: неперекрывающиеся словари, отображающие 252 короткие строки в целые числа и наоборот:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z1=dict(x.items() + y.items())'
1000 loops, best of 3: 260 usec per loop
% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z2=dict(x, **y)'               
10000 loops, best of 3: 26.9 usec per loop

z2 выигрывает примерно в 10 раз. Это довольно большая победа в моей книге!

После сравнения этих двух я подумал, можно ли объяснить низкую производительность z1 из-за накладных расходов на создание двух списков элементов, что, в свою очередь, заставило меня задуматься, может ли этот вариант работать лучше:

from itertools import chain
z3 = dict(chain(x.iteritems(), y.iteritems()))

Несколько быстрых тестов, например

% python -m timeit -s 'from itertools import chain; from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z3=dict(chain(x.iteritems(), y.iteritems()))'
10000 loops, best of 3: 66 usec per loop

Привести меня к выводу, что z3 несколько быстрее, чем z1, но не так быстро, как z2. Определенно не стоит всего лишнего набора текста.

В этом обсуждении все еще отсутствует что-то важное, а именно сравнение производительности этих альтернатив с «очевидным» способом объединения двух списков: с помощью метода update. Чтобы попытаться удержать вещи в равных условиях с выражениями, ни одно из которых не изменяет x или y, я собираюсь сделать копию x вместо ее изменения на месте следующим образом:

z0 = dict(x)
z0.update(y)

Типичный результат:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z0=dict(x); z0.update(y)'
10000 loops, best of 3: 26.9 usec per loop

Другими словами, z0 и z2 имеют практически одинаковую производительность. Как вы думаете, это может быть совпадением? Я не....

На самом деле, я бы зашел так далеко, что утверждал, что чистый код Python не может работать лучше, чем этот. И если вы можете значительно улучшить работу модуля расширения C, я думаю, что пользователи Python вполне могут быть заинтересованы во включении вашего кода (или варианта вашего подхода) в ядро Python. Python использует dict во многих местах; Оптимизация его операций это большое дело.

Вы также можете написать это как

z0 = x.copy()
z0.update(y)

Как и Тони, но (что неудивительно) разница в обозначениях не оказывает заметного влияния на производительность. Используйте то, что вам подходит. Конечно, он абсолютно прав, указывая, что версия с двумя утверждениями намного легче понять.

143
the Tin Man 10 Янв 2015 в 02:32