Preciso mesclar vários dicionários, eis o que tenho por exemplo:
dict1 = {1:{"a":{A}}, 2:{"b":{B}}}
dict2 = {2:{"c":{C}}, 3:{"d":{D}}
Com A
B
C
e D
sendo folhas da árvore, como{"info1":"value", "info2":"value2"}
Existe um nível desconhecido (profundidade) de dicionários, pode ser {2:{"c":{"z":{"y":{C}}}}}
No meu caso, representa uma estrutura de diretório / arquivos com nós sendo docs e deixando de ser arquivos.
Quero mesclá-los para obter:
dict3 = {1:{"a":{A}}, 2:{"b":{B},"c":{C}}, 3:{"d":{D}}}
Não tenho certeza de como eu poderia fazer isso facilmente com o Python.
python
dictionary
merge
array-merge
fdhex
fonte
fonte
y
achatado até oc
nível ou o quê? Seu exemplo está incompleto.Respostas:
isso é realmente bastante complicado - especialmente se você deseja uma mensagem de erro útil quando as coisas são inconsistentes, enquanto aceita corretamente entradas duplicadas mas consistentes (algo que nenhuma outra resposta aqui faz ...)
supondo que você não tenha um grande número de entradas, uma função recursiva é mais fácil:
note que isso muda
a
- o conteúdo deb
é adicionado aa
(que também é retornado). se você quiser ficar,a
pode chamar assimmerge(dict(a), b)
.A agf indicou (abaixo) que você pode ter mais de dois dictos; nesse caso, você pode usar:
onde tudo será adicionado ao dict1.
[nota - editei minha resposta inicial para alterar o primeiro argumento; isso facilita a explicação da "redução"
ps em python 3, você também precisará
from functools import reduce
fonte
reduce
loop equivalente ou para trabalhar com um número arbitrário dedict
s em vez de dois. No entanto, também não tenho certeza de que ele faça o que ele quer (ele não estava claro). Você acaba com2: {'c': {'z': {'y': {'info1': 'value', 'info2': 'value2'}}}, 'b': {'info1': 'value', 'info2': 'value2'}}
o segundo exemplo dele, não tenho certeza se ele querz
e sey
achatou ou não?a[key] = a[key] + b[key]
. Obrigado pela resposta útil.copy.deepcopy
.Aqui está uma maneira fácil de fazer isso usando geradores:
Isso imprime:
fonte
Um problema com essa pergunta é que os valores do ditado podem ser dados arbitrariamente complexos. Com base nessas e em outras respostas, vim com este código:
Meu caso de uso está mesclando arquivos YAML, onde só tenho que lidar com um subconjunto de possíveis tipos de dados. Por isso, posso ignorar tuplas e outros objetos. Para mim, uma lógica de mesclagem sensata significa
Tudo o resto e os imprevistos resultam em um erro.
fonte
isinstance(a, (str, unicode, int, long, float))
não?Como esta é a questão canônica (apesar de certas não-generalidades), estou fornecendo a abordagem pitônica canônica para resolver esse problema.
Caso mais simples: "as folhas são dicts aninhados que terminam em dicts vazios":
Este é o caso mais simples para recursão e eu recomendaria duas abordagens ingênuas:
Acredito que prefiro o segundo ao primeiro, mas lembre-se de que o estado original do primeiro teria que ser reconstruído a partir de sua origem. Aqui está o uso:
Caso complexo: "as folhas são de qualquer outro tipo:"
Portanto, se eles terminam em ditados, é um caso simples de mesclar os ditados vazios finais. Caso contrário, não é tão trivial. Se strings, como você as mescla? Os conjuntos podem ser atualizados da mesma forma, para que possamos dar esse tratamento, mas perdemos a ordem na qual eles foram mesclados. Então, a ordem importa?
Portanto, em vez de mais informações, a abordagem mais simples será fornecer a eles o tratamento padrão de atualização se ambos os valores não forem ditados: ou seja, o valor do segundo ditado substituirá o primeiro, mesmo que o valor do segundo ditado seja Nenhum e o valor do primeiro seja um ditar com muita informação.
E agora
retorna
Aplicação à pergunta original:
Eu tive que remover as chaves em volta das letras e colocá-las entre aspas simples para que isso seja Python legítimo (caso contrário, elas seriam configuradas literais no Python 2.7+), além de acrescentar uma chave ausente:
e
rec_merge(dict1, dict2)
agora retorna:Que corresponde ao resultado desejado da pergunta original (após alterar, por exemplo, o
{A}
para'A'
.)fonte
Com base em @andrew cooke. Esta versão lida com listas aninhadas de dictos e também permite a opção de atualizar os valores
fonte
Esse procedimento recursivo simples mescla um dicionário em outro enquanto substitui chaves conflitantes:
Resultado:
fonte
Com base nas respostas de @andrew cooke. Ele cuida de listas aninhadas de uma maneira melhor.
fonte
Se você tem um nível desconhecido de dicionários, sugiro uma função recursiva:
fonte
Visão geral
A abordagem a seguir subdivide o problema de uma profunda mesclagem de dictos em:
Uma função de mesclagem superficial parametrizada
merge(f)(a,b)
que usa uma funçãof
para mesclar dois dictosa
eb
Uma função de fusão recursiva
f
a ser usada junto commerge
Implementação
Uma função para mesclar dois dictos (não aninhados) pode ser escrita de várias maneiras. Eu pessoalmente gosto
Uma boa maneira de definir uma função de fusão recursiva apropriada
f
é usar multipledispatch, que permite definir funções que avaliam ao longo de caminhos diferentes, dependendo do tipo de argumento.Exemplo
Para mesclar dois dicts aninhados, basta usar,
merge(f)
por exemplo:Notas:
As vantagens dessa abordagem são:
A função é criada a partir de funções menores, cada uma fazendo uma única coisa, o que torna o código mais simples de raciocinar e testar
O comportamento não é codificado, mas pode ser alterado e estendido conforme necessário, o que melhora a reutilização do código (veja o exemplo abaixo).
Costumização
Algumas respostas também consideram ditados que contêm listas, por exemplo, de outros ditos (potencialmente aninhados). Nesse caso, pode-se querer mapear as listas e mesclá-las com base na posição. Isso pode ser feito adicionando outra definição à função de fusão
f
:fonte
Caso alguém queira mais um abordagem para esse problema, aqui está minha solução.
Virtudes : estilo curto, declarativo e funcional (recursivo, sem mutação).
Desvantagem em potencial : talvez essa não seja a fusão que você está procurando. Consulte a documentação para semântica.
fonte
Você pode tentar a fusão .
Instalação
Uso
fonte
Há um pequeno problema com a resposta de andrew cookes: Em alguns casos, ele modifica o segundo argumento
b
quando você modifica o ditado retornado. Especificamente, é por causa dessa linha:Se
b[key]
for adict
, ele será simplesmente atribuído aa
, o que significa que qualquer modificação subsequentedict
afetará ambosa
eb
.Para corrigir isso, a linha teria que ser substituída por esta:
Onde
clone_dict
fica:Ainda. Obviamente, isso não explica
list
,set
e outras coisas, mas espero que ilustre as armadilhas ao tentar mesclardicts
.E, para completar, aqui está a minha versão, onde você pode transmiti-la várias
dicts
:fonte
deepcopy
vez declone_dict
?Esta versão da função contabilizará N número de dicionários e apenas dicionários - nenhum parâmetro impróprio pode ser passado ou gerará um TypeError. A mesclagem em si é responsável por conflitos-chave e, em vez de sobrescrever dados de um dicionário mais abaixo na cadeia de mesclagem, cria um conjunto de valores e anexa a ele; nenhum dado é perdido.
Pode não ser o mais eficiente da página, mas é o mais completo e você não perderá nenhuma informação ao mesclar seus ditados 2 a N.
saída: {1: [1, 2], 2: {1: 2, 3: 1}, 4: 4}
fonte
Como o dictviews suporta operações de conjunto, pude simplificar bastante a resposta do jterrace.
Qualquer tentativa de combinar um dict com um não dict (tecnicamente, um objeto com o método 'keys' e um objeto sem o método 'keys') gerará um AttributeError. Isso inclui tanto a chamada inicial para a função quanto as chamadas recursivas. Era exatamente isso que eu queria, então deixei. Você pode capturar facilmente um AttributeErrors emitido pela chamada recursiva e depois gerar qualquer valor que desejar.
fonte
Curto e doce:
Isso funciona como (e é construído sobre) o
dict.update
método Python . Ele retornaNone
(você sempre pode adicionar,return d
se preferir), pois atualiza o ditadod
no local. A inserção de chavesv
substituirá as chaves existentes emd
(não tenta interpretar o conteúdo do ditado).Também funcionará para outros mapeamentos ("dict-like").
fonte
O código dependerá das suas regras para resolver conflitos de mesclagem, é claro. Aqui está uma versão que pode pegar um número arbitrário de argumentos e mesclá-los recursivamente a uma profundidade arbitrária, sem usar nenhuma mutação de objeto. Ele usa as seguintes regras para resolver conflitos de mesclagem:
{"foo": {...}}
tem precedência sobre{"foo": "bar"}
){"a": 1}
,{"a", 2}
e{"a": 3}
em ordem, o resultado será{"a": 3}
)fonte
Eu tinha dois dicionários (
a
eb
) que cada um poderia conter qualquer número de dicionários aninhados. Eu queria mesclá-los recursivamente, comb
precedênciaa
.Considerando os dicionários aninhados como árvores, o que eu queria era:
a
para que todo caminho para cada folhab
seja representado ema
a
se uma folha for encontrada no caminho correspondente emb
b
nós das folhas permanecem folhas.As respostas existentes foram um pouco complicadas para o meu gosto e deixaram alguns detalhes na prateleira. Eu hackeei o seguinte, que passa nos testes de unidade do meu conjunto de dados.
Exemplo (formatado para maior clareza):
Os caminhos
b
que precisavam ser mantidos foram:1 -> 'b' -> 'white'
2 -> 'd' -> 'black'
3 -> 'e'
.a
tinha os caminhos únicos e não conflitantes de:1 -> 'a' -> 'red'
1 -> 'c' -> 'orange' -> 'dog'
para que eles ainda sejam representados no mapa mesclado.
fonte
Eu tenho uma solução iterativa - funciona muito melhor com dictos grandes e muitos deles (por exemplo, jsons etc):
observe que isso usará o valor em d2 para substituir d1, caso eles não sejam os dois dict. (igual ao de python
dict.update()
)alguns testes:
Eu testei com cerca de ~ 1200 dictos - esse método levou 0,4 segundos, enquanto a solução recursiva levou ~ 2,5 segundos.
fonte
Isso deve ajudar na fusão todos os itens de
dict2
dentrodict1
:Teste-o e diga-nos se é isso que você queria.
EDITAR:
A solução acima mencionada mescla apenas um nível, mas resolve corretamente o exemplo dado pelo OP. Para mesclar vários níveis, a recursão deve ser usada.
fonte
for k,v in dict2.iteritems(): dict1.setdefault(k,{}).update(v)
. Mas como o @agf apontou, isso não mescla os dicts aninhados.{'a':'b'}
com{'a':{'c':'d'}
).Venho testando suas soluções e decidi usar esta no meu projeto:
Passar funções como parâmetros é a chave para estender a solução jterrace para se comportar como todas as outras soluções recursivas.
fonte
A maneira mais fácil de pensar é:
Resultado:
fonte
Eu tenho outra solução um pouco diferente aqui:
Por padrão, ele resolve conflitos em favor dos valores do segundo ditado, mas você pode facilmente substituí-lo; com algumas bruxarias, você pode até lançar exceções nele. :).
fonte
fonte
ei ai, eu também tive o mesmo problema, mas pensei em uma solução e vou publicá-la aqui, caso também seja útil para outras pessoas, basicamente mesclando dicionários aninhados e também adicionando valores, para mim eu precisava calcular algumas probabilidades para que isso um funcionou muito bem:
usando o método acima, podemos mesclar:
target = {'6,6': {'6,63': 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 1} , '6,63': {'63, 4 ': 1}}
src = {'5,4': {'4,4': 1}, '5,5': {'5,4': 1}, '4,4': {'4,3': 1} }
e isso se tornará: {'5,5': {'5,4': 1}, '5,4': {'4,4': 1}, '6,6': {'6,63' : 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 2},' 6,63 ': {'63, 4': 1 }}
observe também as alterações aqui:
target = {'6,6': {'6,63': 1}, '6,63': {'63, 4 ': 1}, ' 4,4 ': {' 4,3 ': 1} , '63, 4 ': {' 4,4 ': 1}}
src = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '4,4': {'4,9': 1} , '3,4': {'4,4': 1}, '5,5': {'5,4': 1}}
mescla = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '6,63': {'63, 4 ': 1} , '5,5': {'5,4': 1}, '6,6': {'6,63': 1}, '3,4': {'4,4': 1}, ' 63,4 ': {' 4,4 ': 1}, ' 4,4 ': {' 4,3 ': 1,' 4,9 ': 1} }
não se esqueça de adicionar também a importação para cópia:
fonte
Resultado:
fonte
dê uma olhada no
toolz
pacotedá
fonte
A função a seguir mescla b em a.
fonte
E apenas outra ligeira variação:
Aqui está uma função de atualização profunda baseada em conjunto python3 pura. Ele atualiza dicionários aninhados percorrendo um nível de cada vez e se chama para atualizar cada próximo nível de valores do dicionário:
Um exemplo simples:
fonte
Que tal outra resposta?!? Este também evita mutação / efeitos colaterais:
fonte