web-dev-qa-db-fra.com

Comment valider la structure (ou schéma) du dictionnaire en Python?

J'ai un dictionnaire avec informations de configuration:

my_conf = {
    'version': 1,

    'info': {
        'conf_one': 2.5,
        'conf_two': 'foo',
        'conf_three': False,
        'optional_conf': 'bar'
    }
}

Je veux vérifier si le dictionnaire suit la structure dont j'ai besoin.

Je cherche quelque chose comme ça:

conf_structure = {
    'version': int,

    'info': {
        'conf_one': float,
        'conf_two': str,
        'conf_three': bool
    }
}

is_ok = check_structure(conf_structure, my_conf)

Existe-t-il une solution à ce problème ou une bibliothèque qui pourrait faciliter la mise en œuvre de check_structure?

14
Thyrst'

Sans utiliser les bibliothèques, vous pouvez également définir une fonction récursive simple comme celle-ci:

def check_structure(struct, conf):
    if isinstance(struct, dict) and isinstance(conf, dict):
        # struct is a dict of types or other dicts
        return all(k in conf and check_structure(struct[k], conf[k]) for k in struct)
    if isinstance(struct, list) and isinstance(conf, list):
        # struct is list in the form [type or dict]
        return all(check_structure(struct[0], c) for c in conf)
    Elif isinstance(struct, type):
        # struct is the type of conf
        return isinstance(conf, struct)
    else:
        # struct is neither a dict, nor list, not type
        return False

Cela suppose que la configuration peut avoir des clés qui ne sont pas dans votre structure, comme dans votre exemple.


Mise à jour: la nouvelle version prend également en charge les listes, par exemple. comme 'foo': [{'bar': int}] 

4
tobias_k

Vous pouvez utiliser schema ( PyPi Link )

schema est une bibliothèque de validation des structures de données Python, telles que celles obtenues à partir de fichiers de configuration, de formulaires, de services externes ou d'analyses de ligne de commande, converties de JSON/YAML (ou quelque chose d'autre) en types de données Python.

from schema import Schema, And, Use, Optional, SchemaError

def check(conf_schema, conf):
    try:
        conf_schema.validate(conf)
        return True
    except SchemaError:
        return False

conf_schema = Schema({
    'version': And(Use(int)),
    'info': {
        'conf_one': And(Use(float)),
        'conf_two': And(Use(str)),
        'conf_three': And(Use(bool)),
        Optional('optional_conf'): And(Use(str))
    }
})

conf = {
    'version': 1,
    'info': {
        'conf_one': 2.5,
        'conf_two': 'foo',
        'conf_three': False,
        'optional_conf': 'bar'
    }
}

print(check(conf_schema, conf))
24
Danil Speransky

@tobias_k m'a battu (probablement en termes de temps et de qualité), mais voici une autre fonction récursive pour la tâche qui pourrait être un peu plus facile à suivre pour vous (et moi):

def check_dict(my_dict, check_against):
    for k, v in check_against.items():
        if isinstance(v, dict):
            return check_dict(my_dict[k], v)
        else:
            if not isinstance(my_dict[k], v):
                return False
    return True
0
Ev. Kounis

La nature des dictionnaires, s'ils sont utilisés en python et non exportés au format JSON, est que l'ordre du dictionnaire n'a pas besoin d'être défini. Au lieu de cela, la recherche de clés renvoie des valeurs (d'où un dictionnaire).

Dans les deux cas, ces fonctions doivent vous fournir ce que vous recherchez pour le niveau d'imbrication présent dans les échantillons que vous avez fournis.

#assuming identical order of keys is required

def check_structure(conf_structure,my_conf):
    if my_conf.keys() != conf_structure.keys():
        return False

    for key in my_conf.keys():
        if type(my_conf[key]) == dict:
            if my_conf[key].keys() != conf_structure[key].keys():
                return False

    return True

#assuming identical order of keys is not required

def check_structure(conf_structure,my_conf):
    if sorted(my_conf.keys()) != sorted(conf_structure.keys()):
        return False

    for key in my_conf.keys():
        if type(my_conf[key]) != dict:
            return False
        else:
            if sorted(my_conf[key].keys()) != sorted(conf_structure[key].keys()):
                return False

    return True

Cette solution devrait évidemment être changée si le niveau d’imbrication était supérieur (c’est-à-dire configuré pour évaluer la similarité de la structure des dictionnaires qui ont certaines valeurs en tant que dictionnaires, mais pas les dictionnaires où certaines valeurs, ces derniers dictionnaires sont également des dictionnaires).

0
Will

Vous pouvez construire une structure en utilisant la récursivité:

def get_type(value):
    if isinstance(value, dict):
        return {key: get_type(value[key]) for key in value}
    else:
        return str(type(value))

Et comparez ensuite la structure requise avec votre dictionnaire:

get_type(current_conf) == get_type(required_conf)

Exemple:

required_conf = {
    'version': 1,
    'info': {
        'conf_one': 2.5,
        'conf_two': 'foo',
        'conf_three': False,
        'optional_conf': 'bar'
    }
}

get_type(required_conf)

{'info': {'conf_two': "<type 'str'>", 'conf_one': "<type 'float'>", 'optional_conf': "<type 'str'>", 'conf_three': "<type 'bool'>"}, 'version': "<type 'int'>"}
0
Eugene Soldatov