Eu tenho uma estrutura de dicionário complexa que gostaria de acessar por meio de uma lista de chaves para abordar o item correto.
dataDict = {
"a":{
"r": 1,
"s": 2,
"t": 3
},
"b":{
"u": 1,
"v": {
"x": 1,
"y": 2,
"z": 3
},
"w": 3
}
}
maplist = ["a", "r"]
ou
maplist = ["b", "v", "y"]
Eu criei o seguinte código que funciona, mas tenho certeza de que existe uma maneira melhor e mais eficiente de fazer isso, se alguém tiver uma ideia.
# Get a given data from a dictionary with position provided as a list
def getFromDict(dataDict, mapList):
for k in mapList: dataDict = dataDict[k]
return dataDict
# Set a given data in a dictionary with position provided as a list
def setInDict(dataDict, mapList, value):
for k in mapList[:-1]: dataDict = dataDict[k]
dataDict[mapList[-1]] = value
python
list
dictionary
Kolergy
fonte
fonte
Respostas:
Use
reduce()
para percorrer o dicionário:e reutilize
getFromDict
para encontrar o local para armazenar o valor parasetInDict()
:Todos os itens, exceto o último elemento,
mapList
são necessários para encontrar o dicionário 'pai' ao qual adicionar o valor e, em seguida, use o último elemento para definir o valor na chave correta.Demo:
Observe que o guia de estilo do Python PEP8 prescreve nomes de snake_case para funções . O exemplo acima funciona igualmente bem para listas ou uma mistura de dicionários e listas; portanto, os nomes devem realmente ser
get_by_path()
eset_by_path()
:fonte
try:
,except (KeyError, IndexError): return default_value
em torno da atualreturn
linha.dict.get()
altera a semântica, pois ela retorna emNone
vez de aumentarKeyError
por nomes ausentes. Quaisquer nomes subseqüentes acionam umAttributeError
.operator
é uma biblioteca padrão, não há necessidade de evitá-la aqui.from functools import reduce
.for
loop. Veja a citação do What's New In Python 3.0 .KeyError
) - consulte a resposta da @ eafit para obter uma soluçãoEntão, por que não usar o método sugerido da pergunta de kolergy para obter um valor:
E o código da resposta da @ eafit para definir um valor:
Ambos funcionam diretamente no python 2 e 3
fonte
getFromDict
, podem destruir o chamadordataDict
. Eu fariacopy.deepcopy(dataDict)
primeiro. Obviamente, (como está escrito), esse comportamento é desejado na segunda função.Usar reduzir é inteligente, mas o método de conjunto do OP pode ter problemas se as chaves pai não existirem no dicionário aninhado. Como este é o primeiro post do SO que vi sobre esse assunto na minha pesquisa no google, gostaria de torná-lo um pouco melhor.
O método set em ( Definindo um valor em um dicionário python aninhado, dada uma lista de índices e valores ) parece mais robusto à falta de chaves parentais. Para copiá-lo:
Além disso, pode ser conveniente ter um método que percorra a árvore de chaves e obtenha todos os caminhos de chave absolutos, para os quais eu criei:
Um uso disso é converter a árvore aninhada em um DataFrame do pandas, usando o código a seguir (assumindo que todas as folhas do dicionário aninhado tenham a mesma profundidade).
fonte
nested_set
?Esta biblioteca pode ser útil: https://github.com/akesterson/dpath-python
fonte
Que tal usar funções recursivas?
Para obter um valor:
E para definir um valor:
fonte
Estilo Python puro, sem qualquer importação:
Resultado
fonte
Uma maneira alternativa, se você não deseja gerar erros, se uma das chaves estiver ausente (para que seu código principal possa ser executado sem interrupção):
Nesse caso, se alguma das teclas de entrada não estiver presente, Nenhuma será retornada, o que pode ser usado como uma verificação no código principal para executar uma tarefa alternativa.
fonte
Em vez de ter um desempenho atingido toda vez que você deseja procurar um valor, que tal achatar o dicionário uma vez e simplesmente procurar a chave como
b:v:y
Dessa forma, você pode simplesmente procurar itens usando o
flat_dict['b:v:y']
que lhe dará1
.E, em vez de percorrer o dicionário em cada pesquisa, você pode acelerar isso achatando o dicionário e salvando a saída, para que uma pesquisa a partir do início a frio signifique carregar o dicionário achatado e simplesmente realizar uma pesquisa de chave / valor sem Travessia.
fonte
Resolvido isso com recursão:
Usando seu exemplo:
fonte
Que tal verificar e definir o elemento dict sem processar todos os índices duas vezes?
Solução:
Exemplo de fluxo de trabalho:
Teste
fonte
Muito tarde para a festa, mas publicar caso isso possa ajudar alguém no futuro. Para o meu caso de uso, a seguinte função funcionou melhor. Trabalha para extrair qualquer tipo de dados do dicionário
dict é o dicionário que contém nosso valor
list é uma lista de "etapas" em direção ao nosso valor
fonte
É satisfatório ver essas respostas por ter dois métodos estáticos para definir e obter atributos aninhados. Essas soluções são muito melhores do que usar árvores aninhadas https://gist.github.com/hrldcpr/2012250
Aqui está a minha implementação.
Uso :
Para definir chamada de atributo aninhado
sattr(my_dict, 1, 2, 3, 5) is equal to my_dict[1][2][3][4]=5
Para obter uma chamada de atributo aninhado
gattr(my_dict, 1, 2)
fonte
Sugiro que você use
python-benedict
para acessar itens aninhados usando o caminho da chave.Instale-o usando
pip
:Então:
Aqui está a documentação completa: https://github.com/fabiocaccamo/python-benedict
fonte
Se você também deseja trabalhar com json arbitrário, incluindo listas e dicts aninhados, e lidar bem com caminhos de pesquisa inválidos, aqui está minha solução:
fonte
um método para concatenar seqüências de caracteres:
fonte
Estendendo o @DomTomCat e a abordagem de outras pessoas, esses configuradores e mapeadores funcionais (ou seja, retornam dados modificados por meio de cópia em profundidade sem afetar a entrada) funcionam para o nested
dict
andlist
.normatizador:
mapeador:
fonte
Você pode fazer uso da
eval
função em python.Explicação
Para sua consulta de exemplo:
maplist = ["b", "v", "y"]
nestq
será"nest['b']['v']['y']"
ondenest
está o dicionário aninhado.A
eval
função interna executa a sequência especificada. No entanto, é importante ter cuidado com as possíveis vulnerabilidades que surgem do uso daeval
função. A discussão pode ser encontrada aqui:Na
nested_parse()
função, assegurei-me de que não houvesse__builtins__
globais disponíveis e que apenas a variável local disponível fosse onest
dicionário.fonte
Você pode usar o pydash:
https://pydash.readthedocs.io/en/latest/api.html
fonte