juntar - transformar objeto em dicionario python




Como mesclar dois dicionários em uma única expressão? (20)

Eu tenho dois dicionários Python, e eu quero escrever uma única expressão que retorna esses dois dicionários, mesclados. O método update() seria o que eu preciso, se ele retornasse seu resultado em vez de modificar um dict in-place.

>>> 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 dit final mesclado em z , não em x ?

(Para ser mais claro, o último dict.update() de conflito do dict.update() é o que eu estou procurando também.)


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

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

  • No Python 3.5 ou superior:

    z = {**x, **y}
    w = {'foo': 'bar', 'baz': 'qux', **y}  # merge a dict with literal values
    
  • 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 dicts e queira mesclá-los em um novo dict sem alterar os ditos 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 valores 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 única expressão.

Note que podemos mesclar com notação literal também:

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

e agora:

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

Agora está sendo mostrado como implementado no cronograma de lançamento do 3.5, PEP 478 , e agora chegou ao documento O Quede Novo no Python 3.5 .

No entanto, como muitas organizações ainda estão no Python 2, você pode querer fazer isso de uma maneira compatível com versões anteriores. A maneira classicamente pitônica, disponível no Python 2 e no Python 3.0-3.4, é fazer isso como um processo de duas etapas:

z = merge_two_dicts(x, y)

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

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

Se você ainda não está no Python 3.5, ou precisa escrever código compatível com versões anteriores, e você quer isso em uma única expressão , a abordagem mais eficiente enquanto correta é colocá-lo em uma função:

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

e então você tem uma única expressão:

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

Você também pode criar uma função para mesclar um número indefinido de ditos, de zero a um número muito grande:

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

Essa função funcionará no Python 2 e 3 para todos os dicts. por exemplo, ditos a a g :

>>> 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 os pares de valores de chave em g terão precedência sobre os ditos a a f , e assim por diante.

Críticas de Outras Respostas

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

>>> c = dict(a.items() | b.items())

No Python 2, você cria duas listas na memória para cada dit, cria uma terceira lista na memória com comprimento igual ao comprimento das duas primeiras juntas e depois descarta todas as três listas para criar o dit. No Python 3, isso falhará porque você está adicionando dois objetos dict_items juntos, não duas listas -

>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

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, a união de items() no Python 3 ( viewitems() no Python 2.7) também falhará quando os valores forem objetos que não podem ser vistos (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:

>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}

Este exemplo demonstra o que acontece quando os valores são inalcançáveis:

z = dict(x, **y)

Aqui está um exemplo em que y deve ter precedência, mas o valor de x é retido devido à ordem arbitrária de conjuntos:

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

Outro hack que você não deve usar:

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

Isso usa o construtor dict , e é muito rápido e eficiente de memória (mesmo 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 dict está sendo passado como construtor dit), é difícil de ler, não é o uso pretendido, e por isso não é Pythonic.

Aqui está um exemplo do uso sendo corrigido no django .

Os dicts são destinados a usar chaves hashable (por exemplo, frozensets ou tuplas), mas esse método falha no Python 3 quando as chaves não são strings.

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

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

Eu estou bem em declarar o dict ({}, ** {1: 3}) ilegal, já que afinal é abuso do mecanismo **.

e

Aparentemente dict (x, ** y) está circulando como "cool hack" para "call x.update (y) e return x". Pessoalmente acho mais desprezível do que legal.

É meu entendimento (assim como o entendimento do criador da linguagem ) que o uso pretendido para o dict(**y) é para criar dicionários para fins de legibilidade, por exemplo:

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

ao invés de

{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7

Resposta aos comentários

Apesar do que Guido diz, dict(x, **y) está alinhado com a especificação do dict, que entre. funciona tanto para o Python 2 como para o 3. O fato de isso funcionar apenas para chaves de string é uma conseqüência direta de como os parâmetros de palavra-chave funcionam e não de um short-comming do dict. Nem está usando o operador ** neste lugar um abuso do mecanismo, na verdade ** foi projetado precisamente para passar ditos como palavras-chave.

Novamente, não funciona para 3 quando as teclas não são strings. O contrato de chamada implícito é que os espaços de nomes tomam ditads ordinários, enquanto os usuários devem apenas passar argumentos de palavra-chave que sejam strings. Todos os outros callables reforçaram isso. dict quebrou essa consistência no Python 2:

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

Essa inconsistência foi ruim, dadas outras implementações do Python (Pypy, Jython, IronPython). Assim, foi corrigido no Python 3, já que esse uso poderia ser uma mudança urgente.

Eu submeto 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 dadas certas restrições arbitrárias.

Mais comentários:

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

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 versões anteriores, já que o Python 2 é 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 se fundem recursivamente e fiquei surpreso que ninguém o mencionou. Na minha interpretação da palavra "mesclar", essas respostas descrevem "atualizar um comando com o outro" e não mesclar.

Sim. Devo encaminhá-lo de volta à pergunta, que está pedindo uma mescla superficial de dois dicionários, com os valores do primeiro sendo sobrescritos pelos segundos - em uma única expressão.

Assumindo 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 ditames de nenhuma fonte, e o caminho mais seguro para evitar isso é fazer uma cópia ao atribuir valores. Como as chaves devem ser hash e geralmente são imutáveis, é inútil copiá-las:

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

Uso:

import timeit

Chegar com contingências para outros tipos de valor está muito além do escopo desta questão, então eu vou apontar a minha resposta à pergunta canônica sobre um "Dicionários de dicionários fundidos" .

Menos ad-hocs com desempenho mas correto

Essas abordagens são menos eficientes, mas fornecem um comportamento correto. Eles terão muito menos desempenho do que copy e update ou a nova descompactação, porque fazem a iteração em cada par de valores-chave em um nível mais alto de abstração, mas respeitam a ordem de precedência (os últimos dicts têm precedência)

Você também pode encadear os ditos manualmente dentro de uma compreensão dit:

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

ou no python 2.6 (e talvez em 2.4 quando as expressões geradoras foram introduzidas):

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

itertools.chain encadeará os iteradores sobre os pares de valores-chave na ordem correta:

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

Análise de desempenho

Eu só vou fazer a análise de desempenho dos usos conhecidos por se comportarem corretamente.

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

O seguinte é feito no Ubuntu 14.04

No Python 2.7 (sistema Python):

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

No Python 3.5 (PPA deadsnakes):

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

Recursos sobre dicionários


Recursivamente / atualizar profundamente um dict

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

Demonstração:

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

Saídas:

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

Obrigado rednaw para edições.


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

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

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

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

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

Em python3, o método items não retorna mais uma lista , mas sim uma view , que age como um set. Nesse caso, você precisará obter a união definida, pois a concatenação com + não funcionará:

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

Para o comportamento do tipo python3 na versão 2.7, o método viewitems deve funcionar no lugar dos items :

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

Eu prefiro essa notação de qualquer forma, já que parece mais natural pensar nela como uma operação conjunta estabelecida, em vez de concatenação (como o título mostra).

Editar:

Mais alguns pontos para o python 3. Primeiro, observe que o truque dict(x, **y) não funcionará no python 3, a menos que as chaves em y sejam strings.

Além disso, a answer do Chainmap de Raymond Hettinger é bem elegante, já que pode levar um número arbitrário de dicionários como argumentos, mas a partir dos documentos parece que ele olha sequencialmente através de uma lista de todos os dicts para cada pesquisa:

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

Isso pode atrasá-lo se você tiver muitas pesquisas em seu aplicativo:

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

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.


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

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


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

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

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

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

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

z = dict(x, **y)

Isto irá, como você quiser, colocar o dict final em z , e fazer com que o valor da chave b seja apropriadamente substituído pelo segundo ( y ) dict's value:

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

Se você usa o Python 3, é apenas um pouco mais complicado. Para criar z :

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

Outra opção mais concisa:

% 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

Nota : isso se tornou uma resposta popular, mas é importante ressaltar que, se y tiver alguma chave que não seja de string, o fato de que isso funciona é um abuso de um detalhe 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ã . Por isso, 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 deve ser totalmente evitado.


Se você acha que os lambdas são maus, então 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:

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

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


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

import collections
a = {1: 1, 2: 2}
b = {2: 3, 3: 4}
c = {3: 5}

r = dict(collections.ChainMap(a, b, c))
print(r)

E é uso:

{1: 1, 2: 2, 3: 4}

Uma alternativa:

% 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


Com base em idéias aqui e em outros lugares, eu compreendi uma função:

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}

Uso (testado em python 3):

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

Você poderia usar um lambda em vez disso.


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

 >>> 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 de "expressão única", como nenhuma função extra é necessária, e é curta.


O problema que tenho com as soluções listadas até agora é que, no dicionário mesclado, o valor da chave "b" é 10, mas, na minha opinião, deveria ser 12. À essa luz, apresento o seguinte:

from functools import reduce

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

Resultados:

dict1 = {'a':1}
dict2 = {'b':2}
new_dict = {**dict1, **dict2}
>>>new_dict
{'a':1, 'a':2}

Embora as respostas sejam boas para esse dicionário superficial , nenhum dos métodos definidos aqui faz uma mesclagem de dicionário profunda.

Exemplos a seguir:

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

Seria de esperar um resultado de algo como isto:

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

Em vez disso, temos isso:

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

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:

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

Resulta em:

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

A mesclagem profunda que o rcwesick deu também cria o mesmo resultado.

Sim, funcionará para mesclar os dicionários de exemplo, mas nenhum deles é um mecanismo genérico para mesclar. Eu atualizarei isso mais tarde assim que eu escrever um método que faça uma mesclagem verdadeira.


No Python 3.5, você pode descompactar **para criar um novo dicionário. Este método não foi mostrado em respostas anteriores. Além disso, é melhor usar em {}vez de dict(). Porque {}é um literal python e dict()envolve uma chamada de função.

 dict1 = {'a':1} dict2 = {'b':2} new_dict = {**dict1, **dict2} >>>new_dict {'a':1, 'a':2} 

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

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

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

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

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

dá:

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






merge