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
もっと簡単に?
ライブラリを使用せずに、次のような単純な再帰関数を定義することもできます。
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}]
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))
@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
辞書の性質は、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
ネストのレベルが大きい場合、このソリューションを明らかに変更する必要があります(つまり、辞書としていくつかの値を持つ辞書の構造の類似性を評価するように構成されていますが、これらの後者の辞書もいくつかの値が辞書である辞書は評価しません)。
再帰を使用して構造を構築できます。
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'>"}