Como mesclar dois dicionários Python em uma única expressão?
Para dicionários x
e y
, z
torna-se um dicionário superficialmente mesclado com valores da y
substituição daqueles de x
.
No Python 3.5 ou superior:
z = {**x, **y}
No Python 2, (ou 3.4 ou menos) escreva uma função:
def merge_two_dicts(x, y):
z = x.copy() # start with x's keys and values
z.update(y) # modifies z with y's keys and values & returns None
return z
e agora:
z = merge_two_dicts(x, y)
No Python 3.9.0a4 ou superior (data de lançamento final aproximadamente outubro de 2020): PEP-584 , discutido aqui , foi implementado para simplificar ainda mais isso:
z = x | y # NOTE: 3.9+ ONLY
Explicação
Digamos que você tenha dois ditados e deseja mesclá-los em um novo ditado sem alterar os ditados originais:
x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
O resultado desejado é obter um novo dicionário (z
) com os valores mesclados, e os valores do segundo ditado substituindo os do primeiro.
>>> z
{'a': 1, 'b': 3, 'c': 4}
Uma nova sintaxe para isso, proposta no PEP 448 e disponível a partir do Python 3.5 , é
z = {**x, **y}
E é de fato uma expressão única.
Observe que também podemos nos fundir com notação literal:
z = {**x, 'foo': 1, 'bar': 2, **y}
e agora:
>>> z
{'a': 1, 'b': 3, 'foo': 1, 'bar': 2, 'c': 4}
Agora está sendo exibido conforme implementado no cronograma de lançamento do 3.5, PEP 478 , e agora chegou ao What's New in Python 3.5 .
No entanto, como muitas organizações ainda estão no Python 2, convém fazer isso de uma maneira compatível com versões anteriores. A maneira classicamente Pythonic, disponível no Python 2 e Python 3.0-3.4, é fazer isso como um processo de duas etapas:
z = x.copy()
z.update(y) # which returns None since it mutates z
Em ambas as abordagens, y
virá segunda e seus valores irá substituir x
's valores, portanto, 'b'
irá apontar para3
nosso resultado final.
Ainda não está no Python 3.5, mas quer um única expressão
Se você ainda não está no Python 3.5 ou precisa escrever um código compatível com versões anteriores, e deseja isso em uma única expressão , o melhor desempenho enquanto a abordagem correta é colocá-lo em uma função:
def merge_two_dicts(x, y):
"""Given two dicts, merge them into a new dict as a shallow copy."""
z = x.copy()
z.update(y)
return z
e então você tem uma única expressão:
z = merge_two_dicts(x, y)
Você também pode criar uma função para mesclar um número indefinido de dictos, de zero a um número muito grande:
def merge_dicts(*dict_args):
"""
Given any number of dicts, shallow copy and merge into a new dict,
precedence goes to key value pairs in latter dicts.
"""
result = {}
for dictionary in dict_args:
result.update(dictionary)
return result
Esta função funcionará no Python 2 e 3 para todos os dictos. por exemplo, ditados dados a
para g
:
z = merge_dicts(a, b, c, d, e, f, g)
e os pares de valores-chave g
terão precedência sobre os ditados a
para f
e assim por diante.
Críticas de outras respostas
Não use o que vê na resposta anteriormente aceita:
z = dict(x.items() + y.items())
No Python 2, você cria duas listas na memória para cada ditado, cria uma terceira lista na memória com comprimento igual ao comprimento dos dois primeiros juntos e, em seguida, descarta as três listas para criar o ditado. No Python 3, isso falhará porque você adiciona dois dict_items
objetos, não duas listas -
>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'
e você teria que criá-los explicitamente como listas, por exemplo z = dict(list(x.items()) + list(y.items()))
. Isso é um desperdício de recursos e poder computacional.
Da mesma forma, aceitar a união do items()
Python 3 ( viewitems()
no Python 2.7) também falhará quando os valores forem objetos laváveis (como listas, por exemplo). Mesmo que seus valores sejam hasháveis, uma vez que os conjuntos são semanticamente desordenados, o comportamento é indefinido em relação à precedência. Então não faça isso:
>>> c = dict(a.items() | b.items())
Este exemplo demonstra o que acontece quando os valores são laváveis:
>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Aqui está um exemplo em que y deve ter precedência, mas o valor de x é retido devido à ordem arbitrária dos conjuntos:
>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}
Outro truque que você não deve usar:
z = dict(x, **y)
Isso usa o dict
construtor e é muito rápido e eficiente em memória (até um pouco mais do que o nosso processo de duas etapas), mas a menos que você saiba exatamente o que está acontecendo aqui (ou seja, o segundo ditado está sendo passado como argumentos de palavra-chave para o ditado construtor), é difícil de ler, não é o uso pretendido e, portanto, não é Pythonic.
Aqui está um exemplo do uso sendo remediado no django .
Os dicts destinam-se a levar chaves hash (por exemplo, frozensets ou tuplas), mas esse método falha no Python 3 quando as chaves não são cadeias de caracteres.
>>> c = dict(a, **b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings
Da lista de discussão , Guido van Rossum, o criador da linguagem, escreveu:
Estou bem em declarar o dict ({}, ** {1: 3}) ilegal, pois, afinal, é abuso do mecanismo **.
e
Aparentemente, o dict (x, ** y) está sendo exibido como "cool hack" para "chame x.update (y) e retorne x". Pessoalmente, acho isso mais desprezível do que legal.
Entendo (assim como o criador da linguagem ) que o uso pretendido dict(**y)
é para criar dictos para fins de legibilidade, por exemplo:
dict(a=1, b=10, c=11)
ao invés de
{'a': 1, 'b': 10, 'c': 11}
Resposta a comentários
Apesar do que Guido diz, dict(x, **y)
está alinhado com a especificação de ditado, que se encaixa. funciona para Python 2 e 3. O fato de que isso funciona apenas para chaves de seqüência de caracteres é uma conseqüência direta de como os parâmetros de palavra-chave funcionam e não um ditado. O uso do ** operador neste local também não é um abuso do mecanismo, de fato ** foi projetado precisamente para passar dict como palavras-chave.
Novamente, ele não funciona para 3 quando as chaves não são cadeias de caracteres. O contrato de chamada implícita é que os namespaces assumem dicionários comuns, enquanto os usuários devem passar apenas argumentos de palavras-chave que são strings. Todos os outros callables o aplicaram. dict
quebrou essa consistência no Python 2:
>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}
Essa inconsistência foi ruim, devido a outras implementações do Python (Pypy, Jython, IronPython). Portanto, ele foi corrigido no Python 3, pois esse uso pode ser uma mudança de última hora.
Eu afirmo a você que é incompetência maliciosa escrever intencionalmente código que funciona apenas em uma versão de um idioma ou que funciona apenas devido a certas restrições arbitrárias.
Mais comentários:
dict(x.items() + y.items())
ainda é a solução mais legível para o Python 2. A legibilidade conta.
Minha resposta: merge_two_dicts(x, y)
na verdade, parece muito mais claro para mim, se estamos realmente preocupados com a legibilidade. E não é compatível com o futuro, pois o Python 2 está cada vez mais obsoleto.
{**x, **y}
parece não manipular dicionários aninhados. o conteúdo das chaves aninhadas é simplesmente sobrescrito, não mesclado [...] acabei sendo queimado por essas respostas que não se mesclam recursivamente e fiquei surpreso que ninguém mencionou. Na minha interpretação da palavra "mesclando", essas respostas descrevem "atualizando um ditado com outro", e não mesclando.
Sim. Devo encaminhá-lo de volta à pergunta, que está pedindo uma mesclagem superficial de dois dicionários, com os valores do primeiro sendo substituídos pelos do segundo - em uma única expressão.
Supondo que dois dicionários de dicionários, um pode recursivamente mesclá-los em uma única função, mas você deve tomar cuidado para não modificar os dictos de nenhuma das fontes, e a maneira mais segura de evitar isso é fazer uma cópia ao atribuir valores. Como as chaves devem ser laváveis e geralmente são imutáveis, não faz sentido copiá-las:
from copy import deepcopy
def dict_of_dicts_merge(x, y):
z = {}
overlapping_keys = x.keys() & y.keys()
for key in overlapping_keys:
z[key] = dict_of_dicts_merge(x[key], y[key])
for key in x.keys() - overlapping_keys:
z[key] = deepcopy(x[key])
for key in y.keys() - overlapping_keys:
z[key] = deepcopy(y[key])
return z
Uso:
>>> x = {'a':{1:{}}, 'b': {2:{}}}
>>> y = {'b':{10:{}}, 'c': {11:{}}}
>>> dict_of_dicts_merge(x, y)
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}
A criação de contingências para outros tipos de valor está muito além do escopo desta pergunta. Por isso, apontarei a minha resposta à pergunta canônica em uma "Mesclagem de dicionários de dicionários" .
Menos desempenho, mas ad-hocs corretos
Essas abordagens têm menos desempenho, mas fornecerão o comportamento correto. Eles serão muito menos eficaz do que copy
e update
ou o novo descompactação porque iterar cada par de valores-chave em um nível mais alto de abstração, mas eles fazer respeitar a ordem de precedência (últimos dicts têm precedência)
Você também pode encadear os dictos manualmente dentro de uma compreensão de dict:
{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7
ou no python 2.6 (e talvez já no 2.4 quando expressões de gerador foram introduzidas):
dict((k, v) for d in dicts for k, v in d.items())
itertools.chain
irá encadear os iteradores sobre os pares de valores-chave na ordem correta:
import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))
Análise de desempenho
Eu só vou fazer a análise de desempenho dos usos conhecidos por se comportar corretamente.
import timeit
O seguinte é feito no Ubuntu 14.04
No Python 2.7 (sistema Python):
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934
No Python 3.5 (PPA mortal):
>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287
Recursos em dicionários
z = x | y