recorrer que ejemplo diccionario python variables object

que - Forma elegante de verificar si existe una clave anidada en un dict de Python



que es un diccionario en python (8)

¿Hay alguna forma más legible de verificar si existe una clave enterrada en un dict sin verificar cada nivel de forma independiente?

Digamos que necesito obtener este valor en un objeto enterrado (ejemplo tomado de Wikidata):

x = s[''mainsnak''][''datavalue''][''value''][''numeric-id'']

Para asegurarse de que esto no termine con un error de tiempo de ejecución, es necesario verificar cada nivel de la siguiente manera:

if ''mainsnak'' in s and ''datavalue'' in s[''mainsnak''] and ''value'' in s[''mainsnak''][''datavalue''] and ''nurmeric-id'' in s[''mainsnak''][''datavalue''][''value'']: x = s[''mainsnak''][''datavalue''][''value''][''numeric-id'']

La otra forma en que puedo pensar para resolver esto es envolver esto en una construcción try catch que siento que también es bastante incómoda para una tarea tan simple.

Estoy buscando algo como:

x = exists(s[''mainsnak''][''datavalue''][''value''][''numeric-id''])

que devuelve True si todos los niveles existen.


Escribí una biblioteca de análisis de datos llamada dataknead para casos como este, básicamente porque el JSON me frustró y la API de Wikidata también regresa.

Con esa biblioteca podrías hacer algo como esto

from dataknead import Knead numid = Knead(s).query("mainsnak/datavalue/value/numeric-id").data() if numid: # Do something with `numeric-id`


Intentar / excepto parece ser la forma más pitónica de hacerlo.
La siguiente función recursiva debería funcionar (devuelve None si no se encontró una de las claves en el dict):

def exists(obj, chain): _key = chain.pop(0) if _key in obj: return exists(obj[_key], chain) if chain else obj[_key] myDict ={ ''mainsnak'': { ''datavalue'': { ''value'': { ''numeric-id'': 1 } } } } result = exists(myDict, [''mainsnak'', ''datavalue'', ''value'', ''numeric-id'']) print(result) >>> 1


La forma de prueba / excepción es la más limpia, sin competencia. Sin embargo, también cuenta como una excepción en mi IDE, que detiene la ejecución durante la depuración.

Además, no me gusta usar excepciones como declaraciones de control en el método, que es esencialmente lo que está sucediendo con el try / catch.

Aquí hay una solución corta que no utiliza la recursividad y admite un valor predeterminado:

def chained_dict_lookup(lookup_dict, keys, default=None): _current_level = lookup_dict for key in keys: if key in _current_level: _current_level = _current_level[key] else: return default return _current_level


Le sugiero que use python-benedict , una subclase sólida de python dict con soporte completo de keypath y muchos métodos de utilidad.

Solo necesita emitir su dict existente:

>>> get({''a'': {''b'': {''c'': [1, 2, 3, 4]}}}, ''a.b.c[1]'') 2

Ahora su dict tiene soporte completo para keypath y puede verificar si la clave existe en la forma pitónica, utilizando el operador in :

s = benedict(s)

Aquí el repositorio de la biblioteca y la documentación: https://github.com/fabiocaccamo/python-benedict


Para ser breve, con Python debes confiar en que es más fácil pedir perdón que permiso

try: x = s[''mainsnak''][''datavalue''][''value''][''numeric-id''] except KeyError: pass

La respuesta

Así es como trato con claves dict anidadas:

def keys_exists(element, *keys): '''''' Check if *keys (nested) exists in `element` (dict). '''''' if type(element) is not dict: raise AttributeError(''keys_exists() expects dict as first argument.'') if len(keys) == 0: raise AttributeError(''keys_exists() expects at least two arguments, one given.'') _element = element for key in keys: try: _element = _element[key] except KeyError: return False return True

Ejemplo:

data = { "spam": { "egg": { "bacon": "Well..", "sausages": "Spam egg sausages and spam", "spam": "does not have much spam in it" } } } print ''spam (exists): {}''.format(keys_exists(data, "spam")) print ''spam > bacon (do not exists): {}''.format(keys_exists(data, "spam", "bacon")) print ''spam > egg (exists): {}''.format(keys_exists(data, "spam", "egg")) print ''spam > egg > bacon (exists): {}''.format(keys_exists(data, "spam", "egg", "bacon"))

Salida:

spam (exists): True spam > bacon (do not exists): False spam > egg (exists): True spam > egg > bacon (exists): True

Se repite en un element dado element prueba cada tecla en un orden dado.

Prefiero esto a todos los métodos variable.get(''key'', {}) que encontré porque sigue a EAFP .

Función excepto para ser llamada como: keys_exists(dict_element_to_test, ''key_level_0'', ''key_level_1'', ''key_level_n'', ..) . Se requieren al menos dos argumentos, el elemento y una clave, pero puede agregar cuántas claves desea.

Si necesita usar un tipo de mapa, puede hacer algo como:

expected_keys = [''spam'', ''egg'', ''bacon''] keys_exists(data, *expected_keys)


Puede usar .get con los valores predeterminados:

s.get(''mainsnak'', {}).get(''datavalue'', {}).get(''value'', {}).get(''numeric-id'')

pero esto es casi seguro menos claro que usar try / except.



Si puede sufrir la prueba de una representación de cadena de la ruta del objeto, entonces este enfoque podría funcionar para usted:

def exists(str): try: eval(str) return True except: return False exists("lst[''sublist''][''item'']")