web-dev-qa-db-ja.com

Pythonの辞書の構造(またはスキーマ)を検証する方法は?

設定情報を含む辞書があります:

my_conf = {
    'version': 1,

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

辞書が必要な構造に従っているかどうかを確認したい。

私はこのようなものを探しています:

conf_structure = {
    'version': int,

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

is_ok = check_structure(conf_structure, my_conf)

この問題に対する解決策や、check_structure もっと簡単に?

17
Thyrst'

ライブラリを使用せずに、次のような単純な再帰関数を定義することもできます。

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

これは、例のように、構成に構造内にないキーを含めることができることを前提としています。


更新:新しいバージョンはリストもサポートしています。 'foo': [{'bar': int}]

6
tobias_k

schemaを使用できます( PyPi Link

schemaは、Python設定ファイル、フォーム、外部サービスから取得したものなどのデータ構造を検証するためのライブラリですまたはJSON/YAML(またはその他)からPython data-typesに変換されたコマンドライン解析。

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))
31
Danil Speransky

@tobias_kは私にそれを打ち負かしました(おそらく時間と品質の両方)が、ここにあなた(そして私)が従うのが少し簡単かもしれないタスクのための別の再帰関数があります:

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

辞書の性質は、pythonで使用され、JSONとしてエクスポートされない場合、辞書の順序を設定する必要はありません。代わりに、キーを検索すると値が返されます。辞書)。

いずれの場合でも、これらの関数は、提供したサンプルに存在するネストのレベルを探しているものを提供する必要があります。

#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

ネストのレベルが大きい場合、このソリューションを明らかに変更する必要があります(つまり、辞書としていくつかの値を持つ辞書の構造の類似性を評価するように構成されていますが、これらの後者の辞書もいくつかの値が辞書である辞書は評価しません)。

0
Will

再帰を使用して構造を構築できます。

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

そして、必要な構造を辞書と比較します。

get_type(current_conf) == get_type(required_conf)

例:

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