Tengo una estructura de diccionario compleja a la que me gustaría acceder a través de una lista de claves para abordar el elemento correcto.
dataDict = {
"a":{
"r": 1,
"s": 2,
"t": 3
},
"b":{
"u": 1,
"v": {
"x": 1,
"y": 2,
"z": 3
},
"w": 3
}
}
maplist = ["a", "r"]
o
maplist = ["b", "v", "y"]
He creado el siguiente código que funciona, pero estoy seguro de que hay una manera mejor y más eficiente de hacerlo si alguien tiene una idea.
# 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
fuente
fuente
Respuestas:
Use
reduce()
para recorrer el diccionario:y reutilizar
getFromDict
para encontrar la ubicación para almacenar el valor desetInDict()
:mapList
Se necesita todo menos el último elemento para encontrar el diccionario 'padre' para agregar el valor, luego use el último elemento para establecer el valor en la clave correcta.Manifestación:
Tenga en cuenta que la guía de estilo Python PEP8 prescribe nombres snake_case para funciones . Lo anterior funciona igualmente bien para listas o una combinación de diccionarios y listas, por lo que los nombres realmente deberían ser
get_by_path()
yset_by_path()
:fuente
try:
,except (KeyError, IndexError): return default_value
alrededor de lareturn
línea actual .dict.get()
cambia la semántica, ya que eso devuelve enNone
lugar de aumentarKeyError
los nombres faltantes. Cualquier nombre posterior entonces desencadena unAttributeError
.operator
es una biblioteca estándar, no hay necesidad de evitarla aquí.from functools import reduce
.for
bucle. Vea la cita de What's New In Python 3.0 .KeyError
); consulte la respuesta de @ eafit para obtener una soluciónEntonces, ¿por qué no usar el método sugerido de la pregunta de kolergy para obtener un valor?
Y el código de la respuesta de @eafit para establecer un valor:
Ambos trabajan directamente en python 2 y 3
fuente
getFromDict
tienen el potencial de destruir a la persona que llamadataDict
. Yocopy.deepcopy(dataDict)
primero. Por supuesto, (como está escrito) este comportamiento se desea en la segunda función.El uso de reducir es inteligente, pero el método de configuración del OP puede tener problemas si las claves principales no existen previamente en el diccionario anidado. Dado que esta es la primera publicación SO que vi para este tema en mi búsqueda de Google, me gustaría mejorarla un poco.
El método set en ( Establecer un valor en un diccionario de Python anidado dada una lista de índices y valores ) parece más robusto para las claves parentales faltantes. Para copiarlo:
Además, puede ser conveniente tener un método que atraviese el árbol de claves y obtenga todas las rutas de clave absolutas, para lo cual he creado:
Uno de sus usos es convertir el árbol anidado en un DataFrame de pandas, utilizando el siguiente código (suponiendo que todas las hojas en el diccionario anidado tengan la misma profundidad).
fuente
nested_set
?Esta biblioteca puede ser útil: https://github.com/akesterson/dpath-python
fuente
¿Qué tal el uso de funciones recursivas?
Para obtener un valor:
Y para establecer un valor:
fuente
Estilo Python puro, sin ninguna importación:
Salida
fuente
Una forma alternativa si no desea generar errores si una de las claves está ausente (para que su código principal pueda ejecutarse sin interrupción):
En este caso, si alguna de las teclas de entrada no está presente, no se devuelve ninguna, que se puede utilizar como un control en su código principal para realizar una tarea alternativa.
fuente
En lugar de tener un éxito en el rendimiento cada vez que desea buscar un valor, ¿qué tal si aplana el diccionario una vez y luego simplemente busca la clave como
b:v:y
De esta manera, simplemente puede buscar elementos usando lo
flat_dict['b:v:y']
que le dará1
.Y en lugar de recorrer el diccionario en cada búsqueda, puede acelerar esto al aplanar el diccionario y guardar la salida para que una búsqueda desde el inicio en frío signifique cargar el diccionario aplanado y simplemente realizar una búsqueda de clave / valor sin el recorrido.
fuente
Resuelto esto con recursividad:
Usando tu ejemplo:
fuente
¿Qué tal verificar y luego establecer el elemento dict sin procesar todos los índices dos veces?
Solución:
Ejemplo de flujo de trabajo:
Prueba
fuente
Muy tarde a la fiesta, pero publicar en caso de que esto pueda ayudar a alguien en el futuro. Para mi caso de uso, la siguiente función funcionó mejor. Funciona para extraer cualquier tipo de datos del diccionario
dict es el diccionario que contiene nuestro valor
lista es una lista de "pasos" hacia nuestro valor
fuente
Es satisfactorio ver estas respuestas para tener dos métodos estáticos para establecer y obtener atributos anidados. Estas soluciones son mucho mejores que usar árboles anidados https://gist.github.com/hrldcpr/2012250
Aquí está mi implementación.
Uso :
Para establecer la llamada de atributo anidado
sattr(my_dict, 1, 2, 3, 5) is equal to my_dict[1][2][3][4]=5
Para obtener una llamada de atributo anidado
gattr(my_dict, 1, 2)
fuente
Le sugiero que use
python-benedict
para acceder a elementos anidados utilizando keypath.Instálelo usando
pip
:Luego:
Aquí la documentación completa: https://github.com/fabiocaccamo/python-benedict
fuente
Si también desea la capacidad de trabajar con json arbitrarios, incluidas listas anidadas y dictados, y manejar muy bien las rutas de búsqueda no válidas, esta es mi solución:
fuente
Un método para concatenar cadenas:
fuente
Extendiendo @DomTomCat y el enfoque de otros, estos configuradores y mapeadores funcionales (es decir, devuelven datos modificados mediante copia profunda sin afectar la entrada) funcionan para anidados
dict
ylist
.setter:
mapeador:
fuente
Puede hacer uso de la
eval
función en python.Explicación
Para su consulta de ejemplo:
maplist = ["b", "v", "y"]
nestq
será"nest['b']['v']['y']"
dondenest
está el diccionario anidado.La
eval
función incorporada ejecuta la cadena dada. Sin embargo, es importante tener cuidado con las posibles vulnerabilidades que surgen del uso de laeval
función. La discusión se puede encontrar aquí:En la
nested_parse()
función, me he asegurado de que no haya__builtins__
globales disponibles y que solo la variable local que esté disponible sea elnest
diccionario.fuente
Puedes usar pydash:
https://pydash.readthedocs.io/en/latest/api.html
fuente