Eu tenho dois dicionários Python e quero escrever uma única expressão que retorne esses dois dicionários, mesclados. O update()
método seria o que eu precisava, se retornasse o resultado em vez de modificar um dicionário no local.
>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> z = x.update(y)
>>> print(z)
None
>>> x
{'a': 1, 'b': 10, 'c': 11}
Como posso obter esse dicionário mesclado final z
, não x
?
(Para ser mais claro, o último que ganha é a manipulação de conflitos, dict.update()
é o que estou procurando também.)
python
dictionary
merge
Carl Meyer
fonte
fonte
z = x | y
Respostas:
Para dicionários
x
ey
,z
torna-se um dicionário superficialmente mesclado com valores day
substituição daqueles dex
.No Python 3.5 ou superior:
No Python 2, (ou 3.4 ou menos) escreva uma função:
e agora:
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:
Explicação
Digamos que você tenha dois ditados e deseja mesclá-los em um novo ditado sem alterar os ditados originais:
O resultado desejado é obter um novo dicionário (
z
) com os valores mesclados, e os valores do segundo ditado substituindo os do primeiro.Uma nova sintaxe para isso, proposta no PEP 448 e disponível a partir do Python 3.5 , é
E é de fato uma expressão única.
Observe que também podemos nos fundir com notação literal:
e agora:
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:
Em ambas as abordagens,
y
virá segunda e seus valores irá substituirx
'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:
e então você tem uma única expressão:
Você também pode criar uma função para mesclar um número indefinido de dictos, de zero a um número muito grande:
Esta função funcionará no Python 2 e 3 para todos os dictos. por exemplo, ditados dados
a
parag
:e os pares de valores-chave
g
terão precedência sobre os ditadosa
paraf
e assim por diante.Críticas de outras respostas
Não use o que vê na resposta anteriormente aceita:
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 -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:Este exemplo demonstra o que acontece quando os valores são laváveis:
Aqui está um exemplo em que y deve ter precedência, mas o valor de x é retido devido à ordem arbitrária dos conjuntos:
Outro truque que você não deve usar:
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.
Da lista de discussão , Guido van Rossum, o criador da linguagem, escreveu:
e
Entendo (assim como o criador da linguagem ) que o uso pretendido
dict(**y)
é para criar dictos para fins de legibilidade, por exemplo:ao invés de
Resposta a comentários
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: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:
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.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:
Uso:
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
eupdate
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:
ou no python 2.6 (e talvez já no 2.4 quando expressões de gerador foram introduzidas):
itertools.chain
irá encadear os iteradores sobre os pares de valores-chave na ordem correta:Análise de desempenho
Eu só vou fazer a análise de desempenho dos usos conhecidos por se comportar corretamente.
O seguinte é feito no Ubuntu 14.04
No Python 2.7 (sistema Python):
No Python 3.5 (PPA mortal):
Recursos em dicionários
fonte
{**{(0, 1):2}}
->{(0, 1): 2}
z = {**x, **y}
realmente estimular mex | y
No seu caso, o que você pode fazer é:
Isso, como você quiser, coloca o comando final
z
e faz com que o valor da chaveb
seja substituído adequadamente pelo valor do segundoy
comando ( ):Se você usa o Python 3, é apenas um pouco mais complicado. Para criar
z
:Se você usa o Python versão 3.9.0a4 ou superior, pode usar diretamente:
fonte
Uma alternativa:
fonte
Update
não é uma das funções "essenciais" que as pessoas costumam usar muito.(lambda z: z.update(y) or z)(x.copy())
: POutra opção mais concisa:
Nota : essa se tornou uma resposta popular, mas é importante ressaltar que, se
y
houver alguma chave não-string, o fato de que isso funcione é um abuso de detalhes de implementação do CPython e não funciona no Python 3, ou em PyPy, IronPython ou Jython. Além disso, Guido não é fã . Portanto, não posso recomendar esta técnica para código portátil compatível com a frente ou de implementação cruzada, o que realmente significa que deve ser totalmente evitado.fonte
Provavelmente, essa não será uma resposta popular, mas você quase certamente não deseja fazer isso. Se você deseja uma cópia mesclada, use copy (ou deepcopy , dependendo do que você deseja) e atualize. As duas linhas de código são muito mais legíveis - mais Pythonic - do que a criação de uma única linha com .items () + .items (). Explícito é melhor que implícito.
Além disso, quando você usa .items () (antes do Python 3.0), está criando uma nova lista que contém os itens do dict. Se seus dicionários forem grandes, isso representa uma sobrecarga considerável (duas listas grandes que serão jogadas fora assim que o ditado mesclado for criado). update () pode funcionar com mais eficiência, porque pode ser executado através do segundo ditado item por item.
Em termos de tempo :
Na OMI, a pequena desaceleração entre os dois primeiros vale a pena pela legibilidade. Além disso, argumentos de palavras-chave para criação de dicionário foram adicionados apenas no Python 2.3, enquanto copy () e update () funcionarão em versões mais antigas.
fonte
Em uma resposta de acompanhamento, você perguntou sobre o desempenho relativo dessas duas alternativas:
Na minha máquina, pelo menos (um x86_64 bastante comum executando o Python 2.5.2), a alternativa
z2
não é apenas mais curta e mais simples, mas também significativamente mais rápida. Você pode verificar isso por si mesmo usando otimeit
módulo que acompanha o Python.Exemplo 1: dicionários idênticos mapeando 20 números inteiros consecutivos para si mesmos:
z2
ganha por um fator de 3,5 ou mais. Dicionários diferentes parecem produzir resultados bastante diferentes, masz2
sempre parecem sair à frente. (Se você obtiver resultados inconsistentes para o mesmo teste, tente passar-r
com um número maior que o padrão 3).Exemplo 2: dicionários sem sobreposição mapeando 252 cadeias curtas para números inteiros e vice-versa:
z2
ganha por um fator de 10. Essa é uma grande vitória no meu livro!Depois de comparar esses dois, perguntei-me se
z1
o baixo desempenho poderia ser atribuído à sobrecarga de construir as duas listas de itens, o que me levou a pensar se essa variação poderia funcionar melhor:Alguns testes rápidos, por exemplo
levou-me a concluir que
z3
é um pouco mais rápido do quez1
, mas não tão rápido quantoz2
. Definitivamente não vale toda a digitação extra.Ainda falta a essa discussão algo importante, que é uma comparação de desempenho dessas alternativas com a maneira "óbvia" de mesclar duas listas: usando o
update
método Para tentar manter as coisas em pé de igualdade com as expressões, nenhuma das quais modifica x ou y, vou fazer uma cópia de x em vez de modificá-la no local, da seguinte maneira:Um resultado típico:
Em outras palavras,
z0
ez2
parecem ter desempenho essencialmente idêntico. Você acha que isso pode ser uma coincidência? Eu não....Na verdade, eu chegaria ao ponto de afirmar que é impossível para o código Python puro fazer melhor que isso. E se você puder se sair significativamente melhor em um módulo de extensão C, imagino que o pessoal do Python possa estar interessado em incorporar seu código (ou uma variação na sua abordagem) ao núcleo do Python. Python usa
dict
em muitos lugares; otimizar suas operações é um grande negócio.Você também pode escrever isso como
como Tony faz, mas (não surpreendentemente) a diferença na notação acaba por não ter nenhum efeito mensurável no desempenho. Use o que parecer melhor para você. Obviamente, ele está absolutamente correto ao apontar que a versão de duas instruções é muito mais fácil de entender.
fonte
items()
não é passível de criação eiteritems
não existe.No Python 3.0 e posterior , você pode usar
collections.ChainMap
quais grupos vários dictos ou outros mapeamentos juntos para criar uma visão única e atualizável:Atualização para o Python 3.5 e posterior : Você pode usar o dicionário estendido do PEP 448 compactando e descompactando. Isso é rápido e fácil:
fonte
del
serão usados e, quando você chamar um, digamos que um ChainMap c exclua o primeiro mapeamento dessa chave.dict
evitar isso, ou seja:dict(ChainMap({}, y, x))
Eu queria algo semelhante, mas com a capacidade de especificar como os valores nas chaves duplicadas foram mesclados, então resolvi isso (mas não o testei muito). Obviamente, essa não é uma expressão única, mas é uma chamada de função única.
fonte
Atualizar um ditado de forma recursiva / profunda
Demonstração:
Saídas:
Obrigado rednaw por edições.
fonte
A melhor versão que eu poderia pensar sem usar a cópia seria:
É mais rápido que,
dict(x.items() + y.items())
mas não tão rápido quanton = copy(a); n.update(b)
, pelo menos no CPython. Esta versão também funciona no Python 3 se você mudariteritems()
paraitems()
, o que é feito automaticamente pela ferramenta 2to3.Pessoalmente, eu gosto mais desta versão porque ela descreve razoavelmente bem o que eu quero em uma única sintaxe funcional. O único pequeno problema é que não fica completamente óbvio que os valores de y têm precedência sobre os valores de x, mas não acredito que seja difícil descobrir isso.
fonte
O Python 3.5 (PEP 448) permite uma opção de sintaxe melhor:
Ou até
No Python 3.9 você também usa | e | = com o exemplo abaixo do PEP 584
fonte
dict(x, **y)
solução-? Como você (@CarlMeyer) mencionou na nota de sua própria resposta ( stackoverflow.com/a/39858/2798610 ), Guido considera essa solução ilegal .dict(x, **y)
pelo (muito bom) motivo de confiary
apenas em chaves que são nomes válidos de argumentos de palavras-chave (a menos que você esteja usando o CPython 2.7, onde o construtor de dict engana). Esta objeção / restrição não se aplica ao PEP 448, que generaliza a**
sintaxe de descompactação para ditar literais. Portanto, esta solução tem a mesma concisãodict(x, **y)
, sem a desvantagem.Para itens com chaves nos dois dicionários ('b'), você pode controlar qual deles termina na saída colocando esse último.
fonte
Embora a pergunta já tenha sido respondida várias vezes, esta solução simples para o problema ainda não foi listada.
É tão rápido quanto z0 e o z2 mal mencionado acima, mas fácil de entender e mudar.
fonte
z4 = {}
e alterar a próxima linha paraz4 = x.copy()
- melhor do que apenas um bom código não faz coisas desnecessárias (o que o torna ainda mais legível e sustentável).Entre essas respostas duvidosas e duvidosas, este exemplo brilhante é a única maneira de mesclar ditados em Python, endossado pelo ditador vitalício Guido van Rossum ! Alguém sugeriu metade disso, mas não o colocou em uma função.
dá:
fonte
Se você acha que as lambdas são más, não leia mais. Conforme solicitado, você pode escrever a solução rápida e com eficiência de memória com uma expressão:
Como sugerido acima, usar duas linhas ou escrever uma função é provavelmente o melhor caminho a percorrer.
fonte
Seja pitônico. Use uma compreensão :
fonte
def dictmerge(*args): return {i:d[i] for d in args for i in d}
z={k: v for d in (x, y) for k, v in d.items()}
No python3, o
items
método não retorna mais uma lista , mas uma exibição , que age como um conjunto. Nesse caso, você precisará fazer a união do conjunto, pois concatenar com+
não funcionará:Para um comportamento semelhante ao python3 na versão 2.7, o
viewitems
método deve funcionar no lugar deitems
:De qualquer forma, prefiro essa notação, pois parece mais natural pensar nela como uma operação de união de conjuntos, em vez de concatenação (como mostra o título).
Editar:
Mais alguns pontos para o python 3. Primeiro, observe que o
dict(x, **y)
truque não funcionará no python 3, a menos que as chavesy
sejam cadeias de caracteres.Além disso, a resposta do Chainmap de Raymond Hettinger é bastante elegante, pois pode usar um número arbitrário de dictes como argumentos, mas a partir dos documentos parece que ele sequencialmente examina uma lista de todos os dictos para cada pesquisa:
Isso pode torná-lo mais lento se você tiver muitas pesquisas em seu aplicativo:
Portanto, uma ordem de magnitude mais lenta para pesquisas. Sou fã do Chainmap, mas parece menos prático onde pode haver muitas pesquisas.
fonte
Abuso que leva a uma solução de expressão única para a resposta de Mateus :
Você disse que queria uma expressão, então eu abusei
lambda
de vincular um nome e as tuplas substituem o limite de expressão única de lambda. Sinta-se livre para encolher.Você também pode fazer isso, é claro, se não se importar em copiá-lo:
fonte
Solução simples usando ferramentas que preservam a ordem (os últimos dicionários têm precedência)
E é uso:
fonte
Dois dicionários
n dicionários
sum
tem um desempenho ruim. Consulte https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/fonte
Embora as respostas sejam boas para este dicionário superficial , nenhum dos métodos definidos aqui realmente faz uma mesclagem profunda de dicionário.
Seguem exemplos:
Seria de esperar um resultado de algo assim:
Em vez disso, obtemos o seguinte:
A entrada 'one' deveria ter 'depth_2' e 'extra' como itens dentro de seu dicionário se realmente fosse uma mesclagem.
Usando cadeia também, não funciona:
Resulta em:
A fusão profunda que rcwesick deu também cria o mesmo resultado.
Sim, funcionará para mesclar os dicionários de amostra, mas nenhum deles é um mecanismo genérico para mesclar. Atualizarei isso mais tarde, quando escrever um método que faça uma verdadeira mesclagem.
fonte
(Somente para Python2.7 *; existem soluções mais simples para Python3 *.)
Se você não tem aversão à importação de um módulo de biblioteca padrão, pode fazer
(O
or a
bit nolambda
é necessário porquedict.update
sempre retornaNone
com sucesso.)fonte
Se você não se importa de mudar
x
,Simples, legível, com bom desempenho. Você sabe
update()
sempre retornaNone
, que é um valor falso. Portanto, a expressão acima sempre será avaliada comox
após a atualização.Os métodos de mutação na biblioteca padrão (como
.update()
) retornamNone
por convenção, portanto esse padrão também funcionará nesses. Se você estiver usando um método que não segue esta convenção,or
talvez não funcione. Mas você pode usar uma exibição e índice de tupla para torná-lo uma única expressão. Isso funciona independentemente do que o primeiro elemento avalia.Se você ainda não possui
x
uma variável, podelambda
criar um local sem usar uma declaração de atribuição. Isso equivale a usarlambda
como expressão let , que é uma técnica comum em linguagens funcionais, mas talvez não sintética.Embora não seja tão diferente do uso a seguir do novo operador de morsa (somente Python 3.8 ou superior):
Se você deseja uma cópia, o estilo PEP 448 é mais fácil
{**x, **y}
. Mas se isso não estiver disponível na sua versão (mais antiga) do Python, o padrão let também funcionará aqui.(Isso é, obviamente, equivalente a
(z := x.copy()).update(y) or z
, mas se a sua versão do Python for nova o suficiente para isso, o estilo PEP 448 estará disponível.)fonte
Com base em idéias aqui e em outros lugares, compreendi uma função:
Uso (testado em python 3):
Você poderia usar um lambda.
fonte
O problema que tenho com as soluções listadas até o momento é que, no dicionário mesclado, o valor da chave "b" é 10, mas, na minha opinião, deve ser 12. Nesse ponto, apresento o seguinte:
Resultados:
fonte
cytoolz.merge_with
( toolz.readthedocs.io/en/latest/… )É tão bobo que
.update
não retorna nada.Eu apenas uso uma função auxiliar simples para resolver o problema:
Exemplos:
fonte
Isso deve resolver seu problema.
fonte
Isso pode ser feito com uma única compreensão de ditado:
Na minha opinião, a melhor resposta para a parte de "expressão única", pois não são necessárias funções extras, e é curta.
fonte
Haverá uma nova opção quando o Python 3.8 for lançado ( programado para 20 de outubro de 2019 ), graças ao PEP 572: Assignment Expressions . O novo operador de expressão de atribuição
:=
permite atribuir o resultado docopy
e ainda usá-lo para chamarupdate
, deixando o código combinado em uma única expressão, em vez de duas instruções, alterando:para:
enquanto se comporta de maneira idêntica em todos os aspectos. Se você também deve retornar o resultado
dict
(você solicitou uma expressão retornando odict
; o acima cria e atribui anewdict
, mas não a retorna, portanto, você não pode usá-la para passar um argumento para uma função como está, à lamyfunc((newdict := dict1.copy()).update(dict2))
) , adicione apenasor newdict
até o final (comoupdate
retornaNone
, que é falso, ele avaliará e retornaránewdict
como resultado da expressão):Advertência importante: Em geral, desencorajaria essa abordagem em favor de:
A abordagem de descompactação é mais clara (para quem sabe sobre descompactação generalizada em primeiro lugar, o que você deveria ), não exige um nome para o resultado (portanto, é muito mais conciso ao construir um temporário que é imediatamente passado para um ou incluída em um
list
/tuple
literal ou similar) e é quase certamente mais rápida, sendo (no CPython) aproximadamente equivalente a:mas feito na camada C, usando a
dict
API concreta , portanto, nenhuma sobrecarga de pesquisa / ligação de método dinâmico ou sobrecarga de despacho de chamada de função está envolvida (onde(newdict := dict1.copy()).update(dict2)
inevitavelmente é idêntico ao comportamento de duas linhas original, executando o trabalho em etapas discretas, com pesquisa dinâmica / ligação / invocação de métodos.Também é mais extensível, pois a fusão de três
dict
s é óbvia:onde o uso de expressões de atribuição não será escalado dessa maneira; o mais próximo que você poderia chegar seria:
ou sem a tupla temporária de
None
s, mas com o teste de veracidade de cadaNone
resultado:é obviamente muito mais feio e inclui outras ineficiências (um desperdício temporário
tuple
deNone
s para separação por vírgula ou teste de veracidade inútil do retorno de cadaupdate
umNone
para aor
separação).A única vantagem real da abordagem de expressão de atribuição ocorre se:
set
s edict
s (ambos suportamcopy
eupdate
, portanto, o código funciona da maneira que você espera)dict
ele próprio, e deve preservar o tipo e a semântica do lado esquerdo (em vez de terminar com um simplesdict
). Emboramyspecialdict({**speciala, **specialb})
possa funcionar, isso envolveria um temporário extradict
e, semyspecialdict
possui recursos simples,dict
não pode preservar (por exemplo,dict
s regulares agora preservam a ordem com base na primeira aparência de uma chave e o valor com base na última aparência de uma chave; você pode querer aquele que preserva a ordem com base no último aparência de uma chave, para que a atualização de um valor também a mova para o final), então a semântica estaria errada. Como a versão da expressão de atribuição usa os métodos nomeados (que provavelmente são sobrecarregados para se comportarem adequadamente), ela nunca cria umdict
de qualquer maneira (a menos quedict1
já tenha sido adict
), preservando o tipo original (e a semântica do tipo original), evitando todos os temporários.fonte
fonte
x
com sua cópia. Sex
é um argumento da função isto não vai funcionar (ver exemplo )