que - recorrer diccionario python




Forma elegante de verificar si existe una clave anidada en un dict de Python (6)

¿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

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)


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']")




object