如何在Python中验证字典的结构(或模式)?

Thy*_*st' 19 python validation schema dictionary config

我有一个配置信息的字典:

my_conf = {
    'version': 1,

    'info': {
        'conf_one': 2.5,
        'conf_two': 'foo',
        'conf_three': False,
        'optional_conf': 'bar'
    }
}
Run Code Online (Sandbox Code Playgroud)

我想检查字典是否遵循我需要的结构.

我正在寻找这样的东西:

conf_structure = {
    'version': int,

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

is_ok = check_structure(conf_structure, my_conf)
Run Code Online (Sandbox Code Playgroud)

是否有任何解决方案可以解决此问题或任何可以使实施check_structure更容易的库?

Dan*_*sky 33

你可以使用schema(PyPi Link)

schema是一个用于验证Python数据结构的库,例如从配置文件,表单,外部服务或命令行解析获得的,从JSON/YAML(或其他)转换为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))
Run Code Online (Sandbox Code Playgroud)

  • @cᴏʟᴅsᴘᴇᴇᴅ 因为它展示了 OP 可能需要的所有功能,并展示了如何在高级条件下使用 lambda。但是你是对的,我会在我的答案中添加一个具体的例子。 (2认同)

fun*_*man 30

对未来的建议:使用 Pydantic!

Pydantic在运行时强制执行类型提示,并在数据无效时提供用户友好的错误。定义数据在纯、规范的 python 中的格式;使用 pydantic 验证它,就这么简单:

from pydantic import BaseModel


class Info(BaseModel):
    conf_one: float
    conf_two: str
    conf_three: bool

    class Config:
        extra = 'forbid'


class ConfStructure(BaseModel):
    version: int
    info: Info
Run Code Online (Sandbox Code Playgroud)

如果验证失败,pydantic 将引发错误并详细说明错误原因:

my_conf_wrong = {
    'version': 1,

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

my_conf_right = {
    'version': 10,

    'info': {
        'conf_one': 14.5,
        'conf_two': 'something',
        'conf_three': False
    }
}

model = ConfStructure(**my_conf_right)
print(model.dict())
# {'version': 10, 'info': {'conf_one': 14.5, 'conf_two': 'something', 'conf_three': False}}

res = ConfStructure(**my_conf_wrong)
# pydantic.error_wrappers.ValidationError: 1 validation error for ConfStructure
#     info -> optional_conf
# extra fields not permitted (type=value_error.extra)
Run Code Online (Sandbox Code Playgroud)


tob*_*s_k 6

不使用库,您还可以定义一个简单的递归函数,如下所示:

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
Run Code Online (Sandbox Code Playgroud)

假定配置可以包含不在您的结构中的键,如您的示例所示。


更新:新版本还支持列表,例如 'foo': [{'bar': int}]