Estou procurando uma maneira de atualizar o dict dictionary1 com o conteúdo da atualização do dict sem substituir o nívelA
dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}}}
update={'level1':{'level2':{'levelB':10}}}
dictionary1.update(update)
print dictionary1
{'level1': {'level2': {'levelB': 10}}}
Eu sei que a atualização exclui os valores no nível2 porque está atualizando o nível de chave mais baixo1.
Como eu poderia resolver isso, considerando que o dictionary1 e a atualização podem ter algum comprimento?
Respostas:
A resposta do @ FM tem a idéia geral correta, ou seja, uma solução recursiva, mas uma codificação um tanto peculiar e pelo menos um bug. Eu recomendo:
Python 2:
Python 3:
Os shows de bugs-se quando o "update" tem um
k
,v
artigo ondev
é umdict
ek
não é originalmente uma chave no dicionário sendo atualizado - @ "salta" da FM código esta parte da atualização (porque ele executa-lo em um novo vaziodict
que não é salvo ou retornado a lugar algum, apenas perdido quando a chamada recursiva retorna).Minhas outras alterações são menores: não há razão para a construção
if
/else
quando.get
faz o mesmo trabalho de maneira mais rápida e limpa, eisinstance
é melhor aplicada a classes base abstratas (não concretas) por generalidade.fonte
isinstance
teste, mas pensei em dar uma facada nele.TypeError: 'int' object does not support item assignment.
quando você, por exemploupdate({'k1': 1}, {'k1': {'k2': 2}})
. Para alterar esse comportamento e, em vez disso, expanda a profundidade dos dicionários para liberar espaço para dicionários mais profundos, você pode adicionar umelif isinstance(d, Mapping):
ao redord[k] = u[k]
daisinstance
condição e depois dela. Você também precisará adicionar umelse: d = {k: u[k]}
para lidar com o caso em que o ditado de atualização é mais profundo que o ditado original. É um prazer editar a resposta, mas não deseja um código conciso sujo que resolva o problema do OP.isinstance(v, collections.Mapping)
vez deisinstance(v, dict)
? Caso o OP decida começar a usar coleções?u.iteritems()
parau.items()
, caso contrário, você encontrará:AttributeError: 'dict' object has no attribute 'iteritems'
Levei-me um pouco neste caso, mas graças ao post de Alex, ele preencheu a lacuna que estava faltando. No entanto, me deparei com um problema se um valor dentro do recursivo
dict
for umlist
, então pensei em compartilhar e estender sua resposta.fonte
orig_dict.get(key, []) + val
.merged_tree = update({'default': {'initialvalue': 1}}, other_tree)
@ A resposta de Alex é boa, mas não funciona ao substituir um elemento como um número inteiro por um dicionário, como
update({'foo':0},{'foo':{'bar':1}})
. Esta atualização aborda:fonte
elif
verificação do tipo de objeto original como um condicional "fechado", contendo as verificações do valor e da chave desse ditado / mapeamento. Inteligente.update({'A1': 1, 'A2':2}, {'A1': {'B1': {'C1': 3, 'C2':4}, 'B2':2}, 'A3':5})
. Você tem um exemplo que não faz o que você quer?if isinstance(d, collections.Mapping)
em todas as iterações? Veja minha resposta .A mesma solução que a aceita, mas a nomenclatura das variáveis mais clara, a sequência de caracteres e corrigiu um erro que,
{}
como valor, não seria substituído.Aqui estão alguns casos de teste:
Esta função está disponível no pacote charlatan , em
charlatan.utils
.fonte
Aqui está uma versão imutável da mesclagem recursiva de dicionário, caso alguém precise.
Com base na resposta de @Alex Martelli .
Python 2.x:
Python 3.x:
fonte
Pequenas melhorias na resposta do @ Alex, que permitem a atualização de dicionários de diferentes profundidades, além de limitar a profundidade que a atualização mergulha no dicionário aninhado original (mas a profundidade do dicionário de atualização não é limitada). Apenas alguns casos foram testados:
fonte
update({'k1': 1}, {'k1': {'k2': {'k3': 3}}})
Eu adicionei uma resposta que aborda estaif isinstance(d, Mapping)
em todas as iterações? Veja minha resposta . (Também não tenho certeza do seud = {k: u[k]}
)Essa pergunta é antiga, mas eu cheguei aqui ao procurar uma solução de "mesclagem profunda". As respostas acima inspiraram o que se segue. Acabei escrevendo o meu próprio porque havia bugs em todas as versões que testei. O ponto crítico perdido foi, em alguma profundidade arbitrária dos dois ditados de entrada, para algumas chaves, k, a árvore de decisão quando d [k] ou u [k] não é um ditado com defeito.
Além disso, esta solução não requer recursão, que é mais simétrica com a forma como
dict.update()
funciona e retornaNone
.fonte
Basta usar
python-benedict
(eu fiz) , ele tem ummerge
método utilitário (deepupdate) e muitos outros. Funciona com python 2 / python 3 e é bem testado.Instalação:
pip install python-benedict
Documentação: https://github.com/fabiocaccamo/python-benedict
fonte
Em nenhuma dessas respostas, os autores parecem entender o conceito de atualizar um objeto armazenado em um dicionário, nem mesmo de iterar sobre os itens do dicionário (em oposição às chaves). Então, eu tive que escrever um que não faça armazenamentos e recuperações inúteis de dicionário tautológico. Presume-se que os dictos armazenem outros dictos ou tipos simples.
Ou ainda mais simples, trabalhando com qualquer tipo:
fonte
Atualize a resposta de @Alex Martelli para corrigir um erro em seu código para tornar a solução mais robusta:
A chave é que geralmente queremos criar o mesmo tipo na recursão; portanto, usamos aqui,
v.copy().clear()
mas não{}
. E isso é especialmente útil se odict
aqui for do tipocollections.defaultdict
que pode ter diferentes tipos dedefault_factory
s.Observe também que o
u.iteritems()
foi alterado parau.items()
inPython3
.fonte
Usei a solução sugerida por Alex Martelli, mas ela falha
TypeError 'bool' object does not support item assignment
quando os dois dicionários diferem no tipo de dados em algum nível.
No mesmo nível, o elemento do dicionário
d
é apenas um escalar (isto é,Bool
) enquanto o elemento do dicionáriou
ainda é o dicionário, a reatribuição falha, pois nenhuma atribuição de dicionário é possível no escalar (comoTrue[k]
).Uma condição adicionada corrige que:
fonte
O código abaixo deve resolver o
update({'k1': 1}, {'k1': {'k2': 2}})
problema da resposta de @Alex Martelli da maneira certa.fonte
use
dict
oucollections.Mapping
fonte
Sei que essa pergunta é bastante antiga, mas ainda estou postando o que faço quando preciso atualizar um dicionário aninhado. Podemos usar o fato de que os dict são passados por referência em python Supondo que o caminho da chave seja conhecido e seja separado por pontos. Forex se tivermos um ditado chamado data:
E queremos atualizar a classe da fila, o caminho da chave seria -
log_config_worker.handlers.queue.class
Podemos usar a seguinte função para atualizar o valor:
Isso atualizaria o dicionário corretamente.
fonte
Pode ser que você tropeça em um dicionário não-padrão, como eu hoje, que não possui iteritems-Attribute. Nesse caso, é fácil interpretar esse tipo de dicionário como um dicionário padrão. Por exemplo: Python 2.7:
Python 3.8:
fonte
Sim! E outra solução. Minha solução difere nas chaves que estão sendo verificadas. Em todas as outras soluções, apenas analisamos as chaves
dict_b
. Mas aqui olhamos para a união dos dois dicionários.Faça como quiser
fonte
Se você deseja substituir um "dicionário aninhado completo por matrizes", pode usar este snippet:
Ele substituirá qualquer "valor antigo" por "novo valor". Ele está fazendo uma reconstrução profunda e profunda do dicionário. Pode até funcionar com List ou Str / int, dado como parâmetro de entrada do primeiro nível.
fonte
Outra maneira de usar a recursão:
fonte
um novo Q como Por uma corrente de chaves
fonte
você pode tentar isso, ele funciona com listas e é puro:
fonte
Eu recomendo substituir
{}
portype(v)()
para propagar o tipo de objeto de qualquer subclasse de dict armazenada,u
mas ausented
. Por exemplo, isso preservaria tipos como coleções.OrderedDict:Python 2:
Python 3:
fonte
Isso é um pouco demais, mas você realmente precisa de dicionários aninhados? Dependendo do problema, às vezes um dicionário plano pode ser suficiente ... e ter uma boa aparência:
fonte
Se você deseja uma linha:
fonte