Pergunta

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 preciso, se ele retornasse seu resultado em vez de modificar um ditado 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 ditado mesclado final z, não x?

(Para ser mais claro, o último vence o tratamento de conflitos de dict.update() é o que estou procurando também.)

Foi útil?

Solução

Como posso mesclar dois dicionários Python em uma única expressão?

Para dicionários x e y, z torna-se um dicionário mesclado superficialmente com valores de y substituindo aqueles de x.

  • Em Python 3.5 ou superior:

    z = {**x, **y}
    
  • No Python 2 (ou 3.4 ou inferior) 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)
    

Explicação

Digamos que você tenha dois dictos e queira mesclá-los em um novo dict sem alterar os dictos 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 dict substituindo os do primeiro.

>>> z
{'a': 1, 'b': 3, 'c': 4}

Uma nova sintaxe para isso, proposta em 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 mesclar com a 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á aparecendo como implementado no cronograma de lançamento para 3.5, PEP 478, e agora chegou ao O que há de novo no Python 3.5 documento.

No entanto, como muitas organizações ainda usam Python 2, você pode querer fazer isso de uma forma compatível com versões anteriores.A maneira clássica de Python, disponível em Python 2 e Python 3.0-3.4, é fazer isso em um processo de duas etapas:

z = x.copy()
z.update(y) # which returns None since it mutates z

Em ambas as abordagens, y virá em segundo lugar e seus valores substituirão xvalores de, portanto 'b' apontará para 3 em nosso resultado final.

Ainda não estou no Python 3.5, mas quero um expressão única

Se você ainda não está no Python 3.5 ou precisa escrever código compatível com versões anteriores e deseja isso em um expressão única, a abordagem mais eficiente e 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á em Python 2 e 3 para todos os dictos.por exemplo.dados dictos a para g:

z = merge_dicts(a, b, c, d, e, f, g) 

e pares de valores-chave em g terá precedência sobre dictos a para f, e assim por diante.

Críticas de outras respostas

Não use o que você vê na resposta aceita anteriormente:

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

No Python 2, você cria duas listas na memória para cada dict, cria uma terceira lista na memória com comprimento igual ao comprimento das duas primeiras juntas e, em seguida, descarta todas as três listas para criar o dict. No Python 3, isso falhará porque você está adicionando dois dict_items objetos juntos, 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 de computação.

Da mesma forma, tomando a união de items() em Python 3 (viewitems() no Python 2.7) também falhará quando os valores forem objetos não laváveis ​​(como listas, por exemplo).Mesmo que seus valores sejam hasháveis, como 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 não podem ser hash:

>>> 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 onde y deveria ter precedência, mas em vez disso 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 hack que você não deve usar:

z = dict(x, **y)

Isso usa o dict construtor e é muito rápido e eficiente em termos de memória (até um pouco mais do que nosso processo de duas etapas), mas a menos que você saiba exatamente o que está acontecendo aqui (ou seja, o segundo dict está sendo passado como argumentos de palavra-chave para o construtor do dict), é difícil de ler, não é o uso pretendido e, portanto, não é Pythonic.

Aqui está um exemplo do uso sendo remediado em Django.

Os dictos destinam-se a usar chaves hash (por exemplo,frozensets ou tuplas), mas este método falha no Python 3 quando as chaves não são strings.

>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

De lista de discussão, Guido van Rossum, o criador da linguagem, escreveu:

Estou bem em declarar o dicto ({}, ** {1: 3}) ilegal, pois, afinal, é abuso do mecanismo **.

e

Aparentemente, o ditado (x, ** y) está acontecendo como "hack legal" para "ligue x.update (y) e retornar x".Pessoalmente, acho mais desprezível do que legal.

É meu entendimento (bem como o entendimento do criador da linguagem) que o uso pretendido para dict(**y) serve para criar dictos para fins de legibilidade, por exemplo:

dict(a=1, b=10, c=11)

em vez de

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

Resposta aos comentários

Apesar do que Guido diz, dict(x, **y) está de acordo com a especificação do dict, que aliás.funciona para Python 2 e 3.O fato de isso funcionar apenas para chaves de string é uma consequência direta de como os parâmetros de palavras-chave funcionam e não uma falha do dict.Usar o operador ** neste local também não é um abuso do mecanismo; na verdade, ** foi projetado precisamente para passar dictos como palavras-chave.

Novamente, não funciona para 3 quando as chaves não são strings.O contrato de chamada implícito é que os namespaces aceitam dictos comuns, enquanto os usuários devem passar apenas argumentos de palavras-chave que sejam 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 era ruim dadas outras implementações de Python (Pypy, Jython, IronPython).Portanto, foi corrigido no Python 3, pois esse uso poderia ser uma mudança significativa.

Afirmo a você que é uma incompetência maliciosa escrever intencionalmente código que só funciona em uma versão de uma linguagem ou que só funciona com certas restrições arbitrárias.

Mais comentários:

dict(x.items() + y.items()) ainda é a solução mais legível para Python 2.A legibilidade conta.

Minha resposta: merge_two_dicts(x, y) na verdade, parece muito mais claro para mim, se estivermos realmente preocupados com a legibilidade.E não é compatível com versões futuras, já que o Python 2 está cada vez mais obsoleto.

{**x, **y} não parece lidar com dicionários aninhados.o conteúdo das chaves aninhadas é simplesmente sobrescrito, não mesclado [...] acabei sendo queimado por essas respostas que não mesclam recursivamente e fiquei surpreso por ninguém ter mencionado isso.Na minha interpretação da palavra "mesclagem", essas respostas descrevem "atualizar um ditado com outro" e não mesclar.

Sim.Devo remetê-lo de volta à pergunta, que é pedir uma raso fusão de dois dicionários, sendo os valores do primeiro substituídos pelos do segundo - em uma única expressão.

Supondo dois dicionários de dicionários, pode-se mesclá-los recursivamente em uma única função, mas você deve ter 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 hasháveis ​​e geralmente imutáveis, é inútil 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: {}}}

Criar contingências para outros tipos de valor está muito além do escopo desta questão, então vou apontar para você minha resposta à pergunta canônica sobre uma "mesclagem de dicionários".

Menos desempenho, mas Ad-hocs corretos

Essas abordagens têm menos desempenho, mas fornecerão um comportamento correto.Eles serão muito menos desempenho do que copy e update ou a nova descompactação porque eles iteram através de cada par de valores-chave em um nível mais alto de abstração, mas eles fazer respeitar a ordem de precedência (os últimos dictos 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 em python 2.6 (e talvez já em 2.4, quando as expressões geradoras foram introduzidas):

dict((k, v) for d in dicts for k, v in d.items())

itertools.chain 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

Farei apenas a análise de desempenho dos usos que se comportam corretamente.

import timeit

O seguinte é feito no Ubuntu 14.04

Em 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

Em Python 3.5 (PPA de cobras mortas):

>>> 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

Outras dicas

No seu caso, o que você pode fazer é:

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

Isso irá, como você deseja, colocar o ditado final em z, e faça o valor da chave b ser devidamente substituído pelo segundo (y) valor do ditado:

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

Se você usar Python 3, será apenas um pouco mais complicado.Para criar z:

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

Uma alternativa:

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

Outra opção mais concisa:

z = dict(x, **y)

Observação:esta se tornou uma resposta popular, mas é importante ressaltar que se y tem chaves que não sejam de string, o fato de isso funcionar é um abuso de um detalhe de implementação do CPython e não funciona no Python 3, ou no PyPy, IronPython ou Jython.Também, Guido não é fã.Portanto, não posso recomendar essa técnica para código portátil compatível com versões futuras ou de implementação cruzada, o que realmente significa que ela deve ser totalmente evitada.

Esta provavelmente não será uma resposta popular, mas é quase certo que você não deseja fazer isso.Se você quiser uma cópia mesclada, use copy (ou cópia profunda, dependendo do que você deseja) e depois 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, ao usar .items() (pré-Python 3.0), você está criando uma nova lista que contém os itens do ditado.Se seus dicionários forem grandes, isso representa uma grande sobrecarga (duas listas grandes que serão descartadas assim que o ditado mesclado for criado).update() pode funcionar com mais eficiência, porque pode executar o segundo ditado item por item.

Em termos de tempo:

>>> 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

Na IMO, 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 só foram adicionados no Python 2.3, enquanto copy() e update() funcionarão em versões mais antigas.

Em uma resposta complementar, você perguntou sobre o desempenho relativo dessas duas alternativas:

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

Na minha máquina, pelo menos (um x86_64 bastante comum rodando Python 2.5.2), alternativa z2 não é apenas mais curto e simples, mas também significativamente mais rápido.Você pode verificar isso por si mesmo usando o timeit módulo que vem com Python.

Exemplo 1:dicionários idênticos mapeando 20 números inteiros consecutivos para si mesmos:

% 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 vence por um fator de 3,5 ou mais.Diferentes dicionários parecem produzir resultados bastante diferentes, mas z2 sempre parece sair na 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 não sobrepostos mapeando 252 strings curtas para números inteiros e vice-versa:

% 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 vence por cerca de um fator de 10.Essa é uma grande vitória no meu livro!

Depois de comparar os dois, me perguntei se z1O fraco desempenho de pode ser atribuído à sobrecarga de construção das duas listas de itens, o que por sua vez me levou a pensar se essa variação poderia funcionar melhor:

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

Alguns testes rápidos, por ex.

% 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

me leva a concluir que z3 é um pouco mais rápido do que z1, mas não tão rápido quanto z2.Definitivamente não vale a pena toda a digitação extra.

Ainda falta algo importante nesta discussão, que é uma comparação de desempenho dessas alternativas com a forma "ó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á-lo no local, como segue:

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

Um resultado típico:

% 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

Em outras palavras, z0 e z2 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 que o código Python puro seja melhor do 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 de 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

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

como Tony faz, mas (não surpreendentemente) a diferença na notação acaba não tendo nenhum efeito mensurável no desempenho.Use o que parecer certo para você.É claro que ele está absolutamente correto ao apontar que a versão de duas afirmações é muito mais fácil de entender.

Eu queria algo semelhante, mas com a capacidade de especificar como os valores nas chaves duplicadas foram mesclados, então hackeei isso (mas não testei muito).Obviamente esta não é uma expressão única, mas é uma chamada de função única.

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

No Python 3, você pode usar coleções.ChainMap que agrupa vários dictos ou outros mapeamentos para criar uma visualização única e atualizável:

>>> 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

Atualização recursiva/profunda de um ditado

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

Demonstração:

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

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

print deepupdate(pluto_original, pluto_update)

Saídas:

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

Obrigado rednaw pelas edições.

A melhor versão que eu poderia pensar sem usar copy seria:

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

É mais rápido do que dict(x.items() + y.items()) mas não tão rápido quanto n = copy(a); n.update(b), pelo menos no CPython.Esta versão também funciona em Python 3 se você alterar iteritems() para items(), o que é feito automaticamente pela ferramenta 2to3.

Pessoalmente, gosto mais desta versão porque descreve muito bem o que desejo em uma única sintaxe funcional.O único pequeno problema é que isso não torna 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.

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

Para itens com chaves em ambos os dicionários ('b'), você pode controlar qual deles termina na saída colocando-o por último.

Python 3.5 (PEP 448) permite uma opção de sintaxe melhor:

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

Ou mesmo

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

Embora a pergunta já tenha sido respondida várias vezes, essa solução simples para o problema ainda não foi listada.

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

É tão rápido quanto z0 e o malvado z2 mencionado acima, mas fácil de entender e mudar.

def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)

Entre essas respostas duvidosas e duvidosas, este exemplo brilhante é a única boa maneira de mesclar dictos em Python, endossado pelo ditador vitalício Guido van Rossum ele mesmo!Alguém sugeriu metade disso, mas não colocou em uma função.

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

dá:

{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}

Se você acha que lambdas são maus, não leia mais.Conforme solicitado, você pode escrever a solução rápida e com uso eficiente de memória com uma expressão:

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}

Como sugerido acima, usar duas linhas ou escrever uma função é provavelmente a melhor opção.

Seja pitônico.Use um compreensão:

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

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

Em python3, o items método não retorna mais uma lista, mas sim um visualizar, que funciona como um conjunto.Neste caso você precisará usar a união set desde a concatenação com + não vai funcionar:

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

Para comportamento semelhante ao python3 na versão 2.7, o viewitems método deve funcionar no lugar de items:

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

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 python 3.Primeiro, observe que o dict(x, **y) truque não funcionará em python 3, a menos que as chaves em y são cordas.

Além disso, o Chainmap de Raymond Hettinger responder é bastante elegante, pois pode receber um número arbitrário de dictos como argumentos, mas dos documentos parece que ele examina sequencialmente uma lista de todos os dictos para cada pesquisa:

As pesquisas pesquisam os mapeamentos subjacentes sucessivamente até que uma chave seja encontrada.

Isso pode atrasar você se você tiver muitas pesquisas em seu aplicativo:

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

Então, cerca de uma ordem de magnitude mais lenta para pesquisas.Sou fã do Chainmap, mas parece menos prático onde pode haver muitas pesquisas.

Abuso levando a uma solução de uma expressão para A resposta de Mateus:

>>> 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}

Você disse que queria uma expressão, então eu abusei lambda para vincular um nome e tuplas para substituir o limite de uma expressão do lambda.Sinta-se à vontade para se encolher.

Você também pode fazer isso, é claro, se não se importar em copiá-lo:

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

Solução simples usando itertools que preserva a ordem (os últimos dictos têm precedência)

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

E é uso:

>>> 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}

Dois dicionários

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

n dicionários

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

sum tem desempenho ruim.Ver https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/

Mesmo que as respostas fossem boas para isso raso dicionário, nenhum dos métodos definidos aqui realmente faz uma mesclagem profunda de dicionário.

Seguem exemplos:

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

Seria de esperar um resultado de algo assim:

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

Em vez disso, obtemos isto:

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

A entrada 'one' deveria ter 'profundidade_2' e 'extra' como itens dentro de seu dicionário se realmente fosse uma mesclagem.

Usar chain também não funciona:

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

Resulta em:

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

A fusão profunda fornecida por rcwesick 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, assim que escrever um método que faça uma mesclagem verdadeira.

Com base em ideias aqui e em outros lugares, compreendi uma função:

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

Uso (testado em 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})

Você poderia usar um lambda.

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, deveria ser 12.Diante disso, apresento o seguinte:

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

Resultados:

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}
>>> 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}

Isso pode ser feito com uma única compreensão de ditado:

>>> 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)
    }

Na minha opinião, a melhor resposta para a parte da 'expressão única', pois não são necessárias funções extras e é curta.

from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

Isso deve resolver seu problema.

(Somente para Python2.7*;existem soluções mais simples para Python3*.)

Se você não tem aversão a importar um módulo de biblioteca padrão, você pode fazer

from functools import reduce

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

(O or a pouco no lambda é necessário porque dict.update sempre retorna None no sucesso.)

É tão bobo isso .update não retorna nada.
Eu apenas uso uma função auxiliar simples para resolver o problema:

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

Exemplos:

merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy

Usando uma compreensão de ditado, você pode

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}

Observe a sintaxe para if else em compreensão

{ (some_key if condition else default_key):(something_if_true if condition 
          else something_if_false) for key, value in dict_.items() }

Eu sei que isso realmente não se enquadra nas especificidades das perguntas ("one liner"), mas como nenhum das respostas acima foram nessa direção, embora muitas e muitas respostas abordassem a questão do desempenho, senti que deveria contribuir com minhas idéias.

Dependendo do caso de uso, pode não ser necessário criar um dicionário mesclado "real" dos dicionários de entrada fornecidos.A visualizar o que faz isso pode ser suficiente em muitos casos, i.e.um objeto que atua como o dicionário mesclado faria isso sem computá-lo completamente.Uma versão preguiçosa do dicionário mesclado, por assim dizer.

Em Python, isso é bastante simples e pode ser feito com o código mostrado no final do meu post.Dado isto, a resposta à pergunta original seria:

z = MergeDict(x, y)

Ao usar este novo objeto, ele se comportará como um dicionário mesclado, mas terá tempo de criação constante e consumo de memória constante, deixando os dicionários originais intactos.Criá-lo é bem mais barato do que nas outras soluções propostas.

É claro que, se você usar muito o resultado, em algum momento você alcançará o limite em que criar um dicionário mesclado real teria sido a solução mais rápida.Como eu disse, depende do seu caso de uso.

Se você já sentiu que preferiria ter uma fusão real dict, então ligando dict(z) iria produzi-lo (mas muito mais caro do que as outras soluções, é claro, então vale a pena mencionar isso).

Você também pode usar esta classe para fazer uma espécie de dicionário copy-on-write:

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}

Aqui está o código direto de 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())
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top