(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优势:
- 提供了对代码的文档注释,包括函数输入的类型和返回类型,类方法的相关信息
- 提高代码可读性、可编辑、可修改性
- 构建和保持一个清晰干净的架构
当然type hint也有劣势:
- 占用更多开发时间
- 启动时间变长,特别是如果使用
typing
包,在短脚本中启动导入的时间显著提升 - 对早期没有引入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
对比Optional
和Union
,可以看到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
包括name
,sex
和types
三个字段,其中sex
可选值只有male
和female
,types
可选值仅包含常见的几种驯化动物。sex
和types
的枚举类型都有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
网友评论