124 votes

Élégante façon de vérifier si une clé imbriquée existe en python dict

Est-il plus lisible pour vérifier si une clé enterré dans un dict existe sans vérifier chaque niveau indépendamment?

Disons que j'ai besoin d'obtenir cette valeur dans un objet enterré (exemple pris sur Wikidata):

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

Pour faire en sorte que cela ne se termine pas par une erreur d'exécution, il est nécessaire de vérifier tous les niveau de la sorte:

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

L'autre manière de penser pour résoudre ce problème est de conclure en try catch construire ce qui me semble aussi assez bizarre pour une telle tâche simple.

Je suis à la recherche de quelque chose comme:

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

qui renvoie True si tous les niveaux existe.

192voto

Arount Points 4988

Pour être bref, avec Python, vous devez faire confiance qu'il est plus facile de demander pardon que demander la permission

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

La réponse

Voici comment je traite avec imbriqué dict clés:

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

Exemple:

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

Sortie:

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

Il boucle en element test de chaque touche dans l'ordre donné.

Je visez plutôt ce à tous variable.get('key', {}) méthodes que j'ai trouvé, car elle suit l'aeap.

Fonction, sauf à être appelé comme: keys_exists(dict_element_to_test, 'key_level_0', 'key_level_1', 'key_level_n', ..). Au moins deux arguments sont requis, l'élément et l'une des clés, mais vous pouvez ajouter le nombre de touches que vous souhaitez.

Si vous devez utiliser le type de carte, vous pouvez faire quelque chose comme:

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

28voto

Daniel Roseman Points 199743

Vous pouvez utiliser .get avec les paramètres par défaut:

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

mais ce n'est presque certainement moins clair que d'utiliser try/except.

11voto

Maurice Meyer Points 4135

Try/except semble être la plus pythonic façon de le faire.
La fonction récursive doit fonctionner (renvoie None si l'une des clés n'a pas été trouvé dans la 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

10voto

Fabio Caccamo Points 6

Je vous suggère d'utiliser python-benedict, un solide python dict sous-classe avec plein de chemin d'accès clé de soutien et de nombreuses méthodes de l'utilitaire.

Vous avez juste besoin de jeter votre dict:

s = benedict(s)

Maintenant, votre dict a plein de chemin d'accès clé de soutien, et vous pouvez vérifier si la clé existe dans le pythonic façon, à l'aide de l'opérateur:

if 'mainsnak.datavalue.value.numeric-id' in s:
    # do stuff

Ici la bibliothèque de référentiel et de la documentation: https://github.com/fabiocaccamo/python-benedict

6voto

Alexander Points 518

Vous pouvez utiliser pydash afin de vérifier s'il existe: http://pydash.readthedocs.io/en/latest/api.html#pydash.objects.has

Ou obtenir la valeur (vous pouvez même définir par défaut - pour le retour si n'existe pas): http://pydash.readthedocs.io/en/latest/api.html#pydash.objects.has

Voici un exemple:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X