美文网首页
Pydantic用type hint实现数据验证, 2023-0

Pydantic用type hint实现数据验证, 2023-0

作者: Mc杰夫 | 来源:发表于2023-05-06 13:45 被阅读0次

    (2023.05.07 Sun @KLN HK)
    几个基本概念:

    • runtime
      有多种含义,1) 用于实现编程语言执行模块的一段代码,该代码允许程序和硬件做互动,往往是编程语言的一部分,不需要单独安装;2) 程序运行的时间,是程序lifecycle的一个阶段,It is the time that a program is running alongside all the external instructions needed for proper execution. runtime有时用于区分于编译,后者称为compile time。
    • type hint
      Python是动态类型语言(dynamically-typed language),编译器只在代码运行时执行类型检测,也因此变量类型可随着运行运行而改变。Type hints在PEP 484被引入到Python中,将静态类型检测的特性带到了代码分析中。Type hint使用Python annotations实现,将类型赋给变量、参数、函数输入、返回值类型、类属性、方法。加入type hints不会耗费runtime。

    type hint优势

    1. 提供了对代码的文档注释,包括函数输入的类型和返回类型,类方法的相关信息
    2. 提高代码可读性、可编辑、可修改性
    3. 构建和保持一个清晰干净的架构

    当然type hint也有劣势

    1. 占用更多开发时间
    2. 启动时间变长,特别是如果使用typing包,在短脚本中启动导入的时间显著提升
    3. 对早期没有引入Python annotations版本的并无显著的type hint效果

    Pydantic

    pydantic是使用python type hints的数据验证工具。一个典型的使用流程是预先定义data model中的字段和类型、是否required等信息,并将输入的数据,如JSON,转化为该data model。如果输入数据与data model中的定义有偏差,则转化时将报错,这样就实现了类静态类型检测的特性。Pydantic常见于FastAPI的应用。

    案例

    # python 3.9
    from datetime import datetime
    from typing import Optional
    from pydantic import BaseModel
    
    class User(BaseModel):
        id: int
        name = 'John Doe'
        signup_ts: Optional[datetime] = None
        friends: list[int] = []
    
    external_data = {
        'id': '123',
        'signup_ts': '2019-06-01 12:22',
        'friends': [1, 2, '3'],
    }
    
    >> user = User(**external_data)
    >> user
    User(id=123, signup_ts=datetime.datetime(2019, 6, 1, 12, 22), friends=[1, 2, 3], name='John Doe')
    >> print(user.id)
    123
    >> print(repr(user.signup_ts))
    datetime.datetime(2019, 6, 1, 12, 22)
    >> print(user.friends)
    [1, 2, 3]
    >> print(user.dict())
    """
    {
        'id': 123,
        'signup_ts': datetime.datetime(2019, 6, 1, 12, 22),
        'friends': [1, 2, 3],
        'name': 'John Doe',
    }
    """
    

    定义data model,继承自pydantic的BaseModel,并在子类中定义变量和类型、默认值。使用typing.Optional方法,表明该变量并非required变量,当用户定义时不提供该变量值则默认为None。另外也可以从typing中引入Union方法,该方法中标识出的类型是数据可能定义的类型,形式如

    a: Union[<data_type1>, <data_type2>, ...]

    from typing import Union
    from pydantic import BaseModel
    
    class test(BaseMode):
        ua: Union[int, str]  # this variable could be int or str
    

    对比OptionalUnion,可以看到Optional[<data_type>]相当于Union[<data_type>, None]

    注意到在Python 3.10 and beyond,Optional可以用|代替。前面的代码在3.10 and beyond可以写成

    from datetime import datetime
    from pydantic import BaseModel
    
    
    class User(BaseModel):
        id: int
        name = 'John Doe'
        signup_ts: datetime | None = None
        friends: list[int] = []
    

    Model的built-in method

    (2023.06.04 Sun KLN HK)
    实例化一个model之后,找到内置方法大致如下

    'construct', 'copy', 'dict', 'from_orm', 'json', 'parse_file', 'parse_obj', 
    'parse_raw', 'schema', 'schema_json', 'update_forward_refs'
    
    • dict方法:用于将实例化的对象转变为dict形式
    • json方法:用于将对象转变为json格式
    >> class User(BaseModel):
    ...     id: int
    ...     name="J D"
    >> u1 = User(id='123')
    >> u1.dict()
    {'id': 123, 'name': 'J D'}
    >> u1.json()
    '{"id": 123, "name": "J D"}'
    >> u1.schema()
    {'title': 'User', 'type': 'object', 'properties': {'id': {'title': 'Id', 'type': 'integer'}, 
    'name': {'title': 'Name', 'default': 'J D', 'type': 'string'}}, 'required': ['id']}
    

    ValidationError

    pydantic提供了数据验证返回的错误类型ValidationError,一旦提供的数据与定义的data model不匹配,则返回该错误。

    from pydantic import ValidationError
    
    try:
        User(signup_ts='broken', friends=[1, 2, 'not number'])
    except ValidationError as e:
        print(e.json())
    

    返回结果如

    [
      {
        "loc": [
          "id"
        ],
        "msg": "field required",
        "type": "value_error.missing"
      },
      {
        "loc": [
          "signup_ts"
        ],
        "msg": "invalid datetime format",
        "type": "value_error.datetime"
      },
      {
        "loc": [
          "friends",
          2
        ],
        "msg": "value is not a valid integer",
        "type": "type_error.integer"
      }
    ]
    

    pydantic和Enum

    (2023.06.17 Sat @SZ 颖隆大厦)
    pydantic通过在model中指定字段可以实现对数据字段的限制。而python的enum结合pydantic使用可以实现对特定字段中可选值的限制,将指定字段的值限制在预先定义的集合中,数据初始化中一旦赋予该字段的值超出预定集合,则初始化将会失败。同时,通过enum的使用,结合FastAPI等工具,可实现下拉列表功能。

    在下面案例中,定义宠物的信息,宠物model Pet包括namesextypes三个字段,其中sex可选值只有malefemaletypes可选值仅包含常见的几种驯化动物。sextypes的枚举类型都有Enum的衍生类来定义。

    from enum import Enum
    from pydantic import BaseModel
    
    class Sex(Enum):
        MALE = "male"
        FEMALE = "female"
    
    class DomesticAnimal(Enum):
        BIRD = "bird"
        CAT = "cat"
        DOG = "dog"
        TURTLE = "turtle"
        FISH = "fish"
    
    class Pet(BaseModel):
        name: str
        sex: Sex
        types: DomesticAnimal
    

    可以使用两种方式定义一个Pet对象。

    >> a1 = {'name': 'Cathy', 'sex': 'female', 'types': 'dog'}
    >> ca = Pet(**a1)
    # or
    >> ca = Pet(name='Cathy', sex='female', types='dog')
    >> ca
    Pet(name='Cathy', sex=<Sex.FEMALE: 'female'>, types=<DomesticAnimal.DOG: 'dog'>)
    

    看到ca对象的结果正常显示。如果在定义时,将其中的types改成没有在DomesticAnimal中定义的种类,则会返回错误。

    >> cheetah = Pet(name="jizhitangjiang", sex="male", types="cheetah")
    ---------------------------------------------------------------------------
    ValidationError                           Traceback (most recent call last)
    /var/folders/d1/s0h2kl014l1bkb0_q0_g5vpr0000gn/T/ipykernel_85550/2575694669.py in <module>
    ----> 1 cheetah = Pet(name="jizhitangjiang", sex="male", types="cheetah")
    
    ~/opt/anaconda3/lib/python3.9/site-packages/pydantic/main.cpython-39-darwin.so in pydantic.main.BaseModel.__init__()
    
    ValidationError: 1 validation error for Pet
    types
      value is not a valid enumeration member; permitted: 'bird', 'cat', 'dog', 'turtle', 'fish' (type=type_error.enum; enum_values=[<DomesticAnimal.BIRD: 'bird'>, <DomesticAnimal.CAT: 'cat'>, <DomesticAnimal.DOG: 'dog'>, <DomesticAnimal.TURTLE: 'turtle'>, <DomesticAnimal.FISH: 'fish'>])
    

    Error message中显示初始化的赋值超出可选范围。

    pydantic的BaseModel结合python Enum可实现对指定字段的可选值的选择。

    Reference

    1 pydantic 点 dev

    相关文章

      网友评论

          本文标题:Pydantic用type hint实现数据验证, 2023-0

          本文链接:https://www.haomeiwen.com/subject/zllcsdtx.html