0%

Python-监测验证

Python 程序度量监测、数据验证的模块。如:

  • timeit:【标准库】运行时间效率测量,精细粒度级别
    • pyheat:可视化显示运行时间,好像不太行,time.sleep(10) 都没检测出来
  • profile:性能调优利器,定位性能/耗时瓶颈。
  • memory_profiler:监控 python 进程的工具,加个装饰器即可输出每行代码内存使用情况
  • pydantic:校验数据的工具
  • typing:【标准库】类型标注支持

timeit

timeit:【标准库】运行时间效率测量,精细粒度级别

pyheat:可视化显示运行时间,好像不太行,time.sleep(10) 都没检测出来

1
2
3
4
5
6
import timeit

def test():
pass

print(timeit.timeit('test()', setup='from __main__ import test',number=1000))

profile

profile:性能调优利器,定位性能/耗时瓶颈。

1
2
3
4
5
6
import profile

def test():
x=2
return x**2
profile.run('test')

memory_profiler

memory_profiler:监控 python 进程的工具,加个装饰器即可输出每行代码内存使用情况

  • 性能测量,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# pip install memory_profiler
import time
from memory_profiler import profile

@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
time.sleep(10)
del b
del a
print("+++++++++")

if __name__ == '__main__':
my_func()
1
2
3
mprof run test.py # 运行
mprof plot # 绘图
mprof clean # 清理

pydantic

pydantic:校验数据的工具

  • 功能:定义数据,自动校验与转换

    • 定义数据:字段、类型、约束
    • 自动类型转换:字符串 “1” 变成数字 1
    • 一致错误格式:统一输出
    • 易用:维护简单(加改字段改类属性即可),IDE还能自动提示
    • 安全可读易维护
  • 将数据的结构声明为具有属性的类,在创建这个类的实例时,值会被校验,并(在需要的情况下)转换为适当的类型,返回一个包含所有数据的对象。

  • 它是一个解析库,不是验证库。验证是达到 建立一个符合所提供的类型 和约束的模型的手段。

    • 保证的是输出数据的类型 与约束,而不是输入类型
    • 虽然验 证不是它的主要目的,但可以用它进行自定义验证
  • 可以更为规范地定义和使用数据接口,对于大型项目的开发更为友好

  • pydantic 在数据传输时会直接进行数据类型转换,因此,如果数据传输格式错误,但是可以通过转换变换为正确的数据类型是,数据传输也可以成功

  • 优点

    • IDE 加成
    • 类型注解定义类的实例
    • 验证请求数据 ,从环境变量中读取系统设置
    • 可以验证复杂结构,validator 装饰器
    • dataclass 装饰器,创建带有输出数据解析和验证的普通 Python 数据类
    • 可递归使用:BaseModel 创建的作为类型

BaseModel+Field声明字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from pydantic import BaseModel
from typing import Dict,List,Sequence,Set,Tuple
class Person(BaseModel):
name:str
a: int # 整型
b: float # 浮点型
c: str # 字符串
d: bool # 布尔型
e: List[int] # 整型列表
f: Dict[str, int] # 字典型,key为str,value为int
g: Set[int] # 集合
h: Tuple[str, int] # 元组

p = Person(name="Tom")
print(p.json()) # {"name": "Tom"} # 已弃用
print(p.model_dump()) # 普通dict
print(p.model_dump_json()) # json字符串

p = {"name": "Tom"}
p = Person(**p)
print(p.json()) # {"name": "Tom"}


class Person:
def __init__(self, name: str):
self.name = name


def get_person_name(one_person: Person):
return one_person.name


# 解析数据
Person.parse_obj() # 传入字典
Person.parse_raw() # 传入str或bytes并将其解析为json,然后将结果传递给parse_obj
Person.parse_file() # 传入文件路径
# 所有字段
person.__fields_set__

# 格式输出
person.dict() # 模型字段和值的字典
person.json() # JSON 字符串表示`dict()`
person.copy() # 浅拷贝
person.parseobj() # _如果对象不是字典,则用于将任何对象加载到具有错误处理的模型中的实用程序_
parseraw() 用于加载多种格式字符串的实用程序;参看。辅助函数
parsefile() 喜欢parseraw()但是对于文件路径;参看。辅助函数
fromorm() 将数据从任意类加载到模型中;参看。ORM模式
schema() 返回将模型表示为 JSON Schema 的字典;参看。图式
schemajson() schema()返回;的 JSON 字符串表示形式 参看。图式
construct() 无需运行验证即可创建模型的类方法;参看。创建没有验证的模型


- dict()
- json()
- copy() 模型的副本(默认为浅表副本)
- parse_obj() 使用dict解析数据
- parse_raw 将strbytes并将其解析为json,然后将结果传递给`parse_obj`
- parse_file 文件路径,读取文件并将内容传递给`parse_raw`。如果`content_type`省略,则从文件的扩展名推断
- from_orm() 从ORM 对象创建模型
- schema() 返回模式的字典
- schema_json() 返回该字典的 JSON 字符串表示
- construct() 允许在没有验证的情况下创建模型
- __fields_set__ 初始化模型实例时设置的字段名称集
- __fields__ 模型字段的字典
- __config__ 模型的配置类

Field 约束:范围 / 长度 / 正则

  • ge / le / gt / lt 类似数学比较:ge = >=,lt = <
  • 正则 pattern 用来匹配格式(可简单理解为“形状过滤器”)
  • description 方便后续生成文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pydantic import BaseModel, Field, ValidationError

def demo_field_constraints():
class Product(BaseModel):
# ... 表示“这个字段必须传”
name: str = Field(..., min_length=2, max_length=30, description="商品名称")
price: float = Field(..., ge=0, le=9999.99, description="价格范围 0~9999.99")
sku: str = Field(..., pattern=r"^[A-Z0-9]{6}$", description="6位大写字母或数字")
stock: int = Field(0, ge=0) # 不传默认为 0;不能为负数

try:
# 字符串 "4999" / "10" 会被自动转成数字
p = Product(name="电脑", price="4999", sku="AB12C3", stock="10")
print("合法商品:", p.model_dump())
except ValidationError as e:
print("错误:", e.errors())

try:
# name 太短;price 负数;sku 不符合大写 + 长度要求
Product(name="X", price=-1, sku="abc123")
except ValidationError as e:
print("约束错误:", e.errors())
  • List[Item] 表示一个“里面全是 Item”的列表,会逐个校验
  • default_factory=list 避免多个实例共用同一列表(经典坑)
  • Dict[str, Any] 表示 key 是字符串,值随意
  • Optional[T] = T 或 None
  • Union[A,B]:按声明顺序尝试匹配
  • Enum:可读性好,不容易拼错
  • Literal:值只能是列出的那些
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from pydantic import BaseModel, ValidationError
from enum import Enum
from typing import Union, Optional, Literal

def demo_union_literal_enum():
class Status(str, Enum):
PENDING = "pending"
DONE = "done"
FAILED = "failed"

class Msg(BaseModel):
id: int
payload: Union[int, str] # 先试 int;失败再试 str
flag: Optional[bool] = None
state: Status # 只能是枚举里的值
level: Literal["low", "medium", "high"] = "low"

m1 = Msg(id=1, payload="123", state="pending")
m2 = Msg(id=2, payload="abc", state=Status.DONE, level="high")
print("消息1:", m1.model_dump())
print("消息2:", m2.model_dump())

try:
# state="other" 不在枚举
Msg(id=3, payload=1.5, state="other")
except ValidationError as e:
print("枚举错误:", e.errors())

# 如果想禁止 float 被当成 str,改成 from pydantic import StrictInt; payload: StrictInt | str

字段级定制校验:field_validator

这里让你“插手”字段处理流程:原始值进来 → 你清洗 / 检查 → 正常转换。

  • mode=”before”:在类型转换前运行(适合清洗原始数据)
  • 返回值必须显式 return,否则后续链条拿不到值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from pydantic import BaseModel, Field, ValidationError, field_validator
from typing import Optional

def demo_field_validator():
class Person(BaseModel):
name: str
age: int = Field(..., ge=0, le=150)
email: Optional[str] = None

@field_validator("name", mode="before")
def trim_name(cls, v):
# 去掉两端空白,再检查是否为空
if isinstance(v, str):
v = v.strip()
if not v:
raise ValueError("姓名不能为空")
return v

@field_validator("email")
def check_email(cls, v):
if v is None:
return v
if "@" not in v:
raise ValueError("邮箱格式错误")
return v.lower()

p = Person(name=" Alice ", age="30", email="A@EXAMPLE.COM")
print("处理后:", p.model_dump())

try:
Person(name=" ", age=10)
except ValidationError as e:
print("字段验证错误:", e.errors())

模型整体逻辑校验:model_validator

当判断需要“看两个或多个字段关系”时,用模型级校验。

预备知识

  • 用于“多个字段之间的关系”检查
  • mode=”after”:先完成字段解析,再整体判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pydantic import BaseModel, ValidationError, model_validator

def demo_model_validator():
class Event(BaseModel):
title: str
start_ts: int
end_ts: int

@model_validator(mode="after")
def check_time(self):
# 逻辑关系:结束必须大于开始
if self.end_ts <= self.start_ts:
raise ValueError("end_ts 必须大于 start_ts")
return self

e = Event(title="会议", start_ts=10, end_ts=20)
print("合法事件:", e.model_dump())

try:
Event(title="错误", start_ts=5, end_ts=5)
except ValidationError as e:
print("模型级错误:", e.errors())

计算属性:computed_field

减少“重复存储 + 手动更新”风险,让值随其它字段自动变化。

预备知识

  • 不用手动存 area;根据已有字段动态算
  • 序列化时自动包含
1
2
3
4
5
6
7
8
9
10
11
12
13
from pydantic import BaseModel, Field, computed_field

def demo_computed_field():
class Rectangle(BaseModel):
width: float = Field(..., gt=0)
height: float = Field(..., gt=0)

@computed_field
def area(self) -> float:
return self.width * self.height

r = Rectangle(width="3.5", height=2) # "3.5" 自动转 3.5
print("矩形:", r.model_dump()) # area 会出现

小结:已经能做“派生值”。接下来学习“批量自动处理字符串 + 控制额外字段”。


model_config:批量字符串清洗与额外字段策略

让“常见重复清洗操作”一行配置搞定。

预备知识

  • str_strip_whitespace:所有字符串字段自动 strip
  • str_to_lower:全部转小写(做账号匹配常用)
  • extra=”forbid”:拒绝未声明字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pydantic import BaseModel, ValidationError

def demo_model_config():
class User(BaseModel):
model_config = {
"str_strip_whitespace": True,
"str_to_lower": True,
"extra": "forbid",
}
username: str
role: str = "user"

u = User(username=" ALICE ")
print("配置转换:", u.model_dump())

try:
User(username="Bob", unknown=1)
except ValidationError as e:
print("额外字段禁止:", e.errors())

小结:会集中配置模型行为。下一步:把“系统环境变量”转成配置对象。


BaseSettings:环境变量配置加载

帮助你把“全部是字符串”的环境变量自动转换成正确类型。

预备知识

  • BaseSettings 会读取系统环境变量
  • env_prefix 设置统一前缀,避免污染
  • 适合应用配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import os
from pydantic_settings import BaseSettings, SettingsConfigDict

def demo_settings_from_env():
# 模拟设置环境变量(真实部署中由系统或容器注入)
os.environ["APP_HOST"] = "127.0.0.1"
os.environ["APP_PORT"] = "8000"
os.environ["APP_DEBUG"] = "true"
os.environ["APP_DB_URL"] = "postgres://user:pass@localhost:5432/db"

class AppSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="APP_")
host: str
port: int
debug: bool = False
db_url: str
timeout: int = 30 # 没有环境变量时用默认

s = AppSettings()
print("加载到的配置:", s.model_dump())

小结:配置来源搞定。下一步:适配“外部字段名”和“内部命名风格”。


alias:外部协议名与内部字段名解耦

应对“外部 JSON 命名风格不同”问题。

预备知识

  • 外部 JSON 可能字段叫 id,内部想叫 user_id
  • alias 映射;by_alias=True 时导出外部名字
  • populate_by_name 允许用内部名字传值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pydantic import BaseModel, Field

def demo_alias():
class User(BaseModel):
model_config = {"populate_by_name": True}
user_id: int = Field(..., alias="id")
display_name: str = Field(..., alias="nick")

u1 = User(id=1, nick="Tom") # 用外部名字
u2 = User(user_id=2, display_name="Jerry") # 用内部名字

print("原字段序列化(内部名字):", u1.model_dump())
print("使用别名输出(外部风格):", u1.model_dump(by_alias=True))
print("混合输入结果:", u2.model_dump())

小结:命名桥梁建立。下一步:自定义特殊类型 + 校验。


自定义类型与专用校验

当基础类型语义不够,自己包装一个,提升可读性与复用。

预备知识

  • 自己写一个继承自 str 的类型方便语义表达
  • arbitrary_types_allowed 允许模型字段用自定义类型
  • 自定义校验逻辑放在 field_validator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pydantic import BaseModel, Field, ValidationError, field_validator

def demo_custom_error():
class PhoneStr(str):
# 可扩展更多行为,这里先空着
pass

class Contact(BaseModel):
model_config = {"arbitrary_types_allowed": True}
phone: PhoneStr = Field(..., description="手机号,11位数字")

@field_validator("phone")
def check_phone(cls, v):
if not (v.isdigit() and len(v) == 11):
raise ValueError("手机号必须为11位数字")
return v

c = Contact(phone=PhoneStr("13812345678"))
print("联系人:", c.model_dump())
try:
Contact(phone=PhoneStr("abc"))
except ValidationError as e:
print("自定义错误:", e.errors())

小结:语义更清晰。下一步:解析“多形态消息”。


区分联合(多形态数据):discriminator

让多个“形态”共存,并自动识别是哪一种。

预备知识

  • 多种“形态”的消息统一解析
  • discriminator 字段用来“告诉模型我是哪个形态”
  • TypeAdapter 可单独对复杂类型(如 List[Union…])做校验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from pydantic import BaseModel, Field, ValidationError
from typing import Union, Annotated, List, Literal
from pydantic import TypeAdapter

def demo_discriminated_union():
class BaseMsg(BaseModel):
type: str # 仅作为共同基类字段

class TextMsg(BaseMsg):
type: Literal["text"]
content: str

class ImageMsg(BaseMsg):
type: Literal["image"]
url: str
width: int
height: int

# 用 Field(discriminator="type") 指定区分键
MsgUnion = Annotated[Union[TextMsg, ImageMsg], Field(discriminator="type")]
adapter = TypeAdapter(List[MsgUnion])

data = [
{"type": "text", "content": "hello"},
{"type": "image", "url": "xxx.png", "width": 100, "height": 80},
]
msgs = adapter.validate_python(data)
print("区分联合解析:", [m.model_dump() for m in msgs])

try:
# 少了 height 字段
adapter.validate_python([{"type": "image", "url": "x.png", "width": 10}])
except ValidationError as e:
print("联合错误:", e.errors())

小结:多形态搞定。接下来关注“安全更新对象”。


model_copy:不可变风格更新

避免“原数据被随意改坏”,做“复制 + 局部改”。

预备知识

  • 防止原对象被意外改动
  • 类似“复制 + 局部修改”实现“函数式”风格
1
2
3
4
5
6
7
8
9
10
11
from pydantic import BaseModel

def demo_model_copy():
class Acc(BaseModel):
id: int
balance: float

a1 = Acc(id=1, balance=100)
a2 = a1.model_copy(update={"balance": 150})
print("原对象:", a1.model_dump())
print("新对象:", a2.model_dump())

小结:更新方式更安全。下一步引入“严格类型”防止模糊转换。


严格类型:StrictInt / StrictBool

当你不想“字符串 1”混进来假扮数字时,用严格类型。

预备知识

  • 默认 int 会接受 “1”;StrictInt 不会
  • 严格模式用来防“脏数据偷偷混进来”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pydantic import BaseModel, ValidationError, StrictInt, StrictBool

def demo_strict_types():
class StrictModel(BaseModel):
strict_num: StrictInt
strict_flag: StrictBool

try:
# 这里会失败:"1" 不是纯 int;"true" 不是纯 bool
StrictModel(strict_num="1", strict_flag="true")
except ValidationError as e:
print("严格类型错误:", e.errors())

ok = StrictModel(strict_num=1, strict_flag=True)
print("严格通过:", ok.model_dump())

小结:输入更干净。下一步学“输出时挑选字段”。


序列化控制:include / exclude / exclude_none

对外输出时,往往不能“全都给”,需要过滤。

预备知识

  • include:只要谁(白名单)
  • exclude:排除谁(黑名单)
  • exclude_none=True:自动去掉值为 None 的字段(减轻输出噪声)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pydantic import BaseModel
from typing import Optional

def demo_serialization_control():
class User(BaseModel):
id: int
name: str
email: Optional[str] = None
token: str # 例如登录后生成的秘密

u = User(id=1, name="Alice", email=None, token="SECRET")

print("完整:", u.model_dump())
print("排除敏感:", u.model_dump(exclude={"token"}))
print("仅输出部分:", u.model_dump(include={"id", "name"}))
print("exclude_none:", u.model_dump(exclude_none=True))

小结:输出面可控。下一步:提前“丢掉多余垃圾字段”。


预过滤外部“脏数据”:extra = ignore

外部来源常附带你不关心的字段,直接忽略更省心。

预备知识

  • 外部来源(表单/爬虫)经常混入多余字段
  • extra=”ignore”:自动丢弃未声明字段,不报错
1
2
3
4
5
6
7
8
9
10
11
12
13
from pydantic import BaseModel

def demo_pre_filter_data():
raw = {"id": "10", "name": "Bob", "age": 20, "unknown": 1}

class CleanUser(BaseModel):
model_config = {"extra": "ignore"}
id: int
name: str
age: int

user = CleanUser(**raw) # unknown 被忽略
print("外部数据过滤:", user.model_dump())

小结:输入更“干净”后,错误信息也更“聚焦”。最后来规范错误输出格式。


统一错误输出格式

适合统一给前端 / 日志:可读 + 可机器处理。

预备知识

  • e.errors() 列表可统一 reshape 成前端友好格式
  • loc 拼成字符串方便展示路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pydantic import BaseModel, Field, ValidationError

def demo_error_handling_pattern():
class Simple(BaseModel):
a: int
b: int = Field(..., gt=0)

try:
Simple(a="x", b=0)
except ValidationError as e:
errors = [
{
"loc": ".".join(str(p) for p in err["loc"]), # 路径如 "a" / "b"
"msg": err["msg"], # 人类可读信息
"type": err["type"], # 机器可再分类
}
for err in e.errors()
]
print("统一错误格式:", errors)

小结:到这里,你已经掌握从“声明 → 校验 → 嵌套 → 多形态 → 清洗 → 配置 → 严格输出 → 错误格式化”的完整链路。


学习路径建议

  1. 先懂 BaseModel / Field / ValidationError(最核心三件套)。
  2. 再加 field_validator / model_validator 做细与“跨字段”逻辑。
  3. 逐步引入 Settings / discriminator / 严格类型提升质量。
  4. 熟练后用 include/exclude + 错误格式统一进行工程打磨。

动态模型

在某些情况下,直到运行时才知道模型的结构。为此 pydantic 提供了 create_model 允许动态创建模型的方法。

1
2
3
from pydantic import BaseModel, create_model

DynamicFoobarModel = create_model('DynamicFoobarModel', foo=(str, ...), bar=123)

ValidationError看错误结构

validator 验证器

  • field_validator/model validator

  • 验证器是“类方法”,因此它们接收的第一个参数值是 UserModel 类,而不是 UserModel

  • 第二个参数始终是要验证的字段值,可以随意命名

  • 单个验证器可以通过传递多个字段名称来应用于多个字段,也可以通过传递特殊值在所有字段上调用单个验证器 '*'

  • 关键字参数 pre 将导致在其他验证之前调用验证器

  • 通过 each_item=True 将导致验证器被施加到单独的值(例如 ListDictSet 等),而不是整个对象

  • 关键字参数 always 将导致始终验证,出于性能原因,默认情况下,当未提供值时,不会为字段调用验证器。然而,在某些情况下,始终调用验证器可能很有用或需要,例如设置动态默认值。

  • allow_reuse 可以在多个字段/模型上使用相同的验证器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from pydantic import BaseModel, ValidationError, validator


class UserModel(BaseModel):
name: str
username: str
password1: str
password2: str

@validator('name')
def name_must_contain_space(cls, v):
if ' ' not in v:
raise ValueError('must contain a space')
return v.title()

@validator('password2')
def passwords_match(cls, v, values, **kwargs):
if 'password1' in values and v != values['password1']:
raise ValueError('passwords do not match')
return v

@validator('username')
def username_alphanumeric(cls, v):
assert v.isalnum(), 'must be alphanumeric'
return v


user = UserModel(
name='samuel colvin',
username='scolvin',
password1='zxcvbn',
password2='zxcvbn',
)
print(user)
#> name='Samuel Colvin' username='scolvin' password1='zxcvbn' password2='zxcvbn'

try:
UserModel(
name='samuel',
username='scolvin',
password1='zxcvbn',
password2='zxcvbn2',
)
except ValidationError as e:
print(e)
"""
2 validation errors for UserModel
name
must contain a space (type=value_error)
password2
passwords do not match (type=value_error)
"""

Settings、alias、discriminator:工程化

严格类型:include/exclude:质量与输出控制

类型

标准库类型

  • bool
  • int:int(value)
  • float: float(value),强制转换为 float
  • str:字符串按原样接受,int、float 和 Decimal 使用 str(v) 强制转换,bytes 和 bytearray 使用 v.decode() 转换,继承自 str 的枚举使用 v.value 转换,所有其他类型都会导致错误。
  • bytes: 字节是按原格式接受的,bytearray 使用 bytes(v) 进行转换,str 使用 v.encode() 进行转换,int、float 和 Decimal 使用 str(v).encode() 进行转换。
  • typing的类型
  • datetime.date:有关解析和验证的更多细节
  • datetime.time:有关解析和验证的更多细节
  • datetime.datetime:有关解析和验证的更多细节
  • datetime.timedelta:有关解析和验证的更多细节
  • ipaddress.IPv4Address:通过将值传递给 IPv4Address(v),简单地使用类型本身进行验证;有关其他自定义 IP 地址类型
  • ipaddress.IPv4Interface:通过将值传递给 IPv4Interface(v),简单地使用类型本身进行验证;有关其他自定义 IP 地址类型
  • ipaddress.IPv4Network:通过将值传递给 IPv4Network(v),简单地使用类型本身进行验证;有关其他自定义 IP 地址类型
  • ipaddress.IPv6Address:通过将值传递给 IPv6Address(v),简单地使用类型本身进行验证;有关其他自定义 IP 地址类型
  • ipaddress.IPv6Interface:通过将值传递给 IPv6Interface(v),简单地使用类型本身进行验证;有关其他自定义 IP 地址类型
  • ipaddress.IPv6.Network:通过将值传递给 IPv6Network(v),简单地使用类型本身进行验证;有关其他自定义 IP 地址类型
  • enum.Enum:检查值是有效的 Enum 实例。
  • subclass of enum.Enum:检查值是有效的枚举成员。更多细节,
  • enum.IntEnum:检查值是有效的 IntEnum 实例。
  • subclass of enum.IntEnum:检查值是有效的整数枚举成员。更多细节,
  • decimal.Decimal:ydantic 会尝试将值转换为字符串,然后将那个字符串传递个 Decimal(v)。:athlib.Path:通过将值传递给 Path(v) ,简单地使用类型本身进行验证。对于其他更严格的路径类型,
  • uuid.UUID:字符串和字节 (转换为字符串) 被传递到 UUID(v),对于 bytes 和 bytearray ,回退到 UUID(bytes=v);有关其他更严格的 UUID 类型
  • ByteSize: 将带有单位的字节字符串转换为字节。

typing 中的可迭代类型

  • Sequence:生成器
  • Union[int, str]:联合类型,允许接受不同的类型
  • Optional[int]:可选类型,相当于 Union[int, None]

Pydantic 类型

  • FilePath:与 Path 类似,但路径必须存在且必须是文件
  • DirectoryPath:与 Path 类似,但路径必须存大且时目录
  • EmailStr:依赖于 email-validator:输入字符串必须是有效的 email 地址,输出简单字符串
  • NameEmail:依赖于 email-validator,输入字符串必须是有效的 email 地址或是 Fred Bloggs <fred.bloggs@example.com> 这样的格式,输出是一个 NameEmail 对象,该对象有两个属性:name 和 email
  • PyObject: 需要一个可调用对象或一个包含 . 的表示导入路径的字符串。如果提供的是一个字符串,则从该字符串所表示的导入路径加载最后一个 . 右边的子串所表示的可导入 Python 对象。 例如,如果提供了 ‘math.cos’,则结果字段值将为函数 cos。如果提供了 pydantic.dataclasses.dataclass,则结果字段值将为 dataclass。
  • Color: 用于解析 HTML 和 CSS 颜色。
  • Json: 一个特殊的类型包装器,在解析之前加载 JSON;
  • PaymentCardNumber: 对支付卡进行解析和验证;
  • AnyUrl: 任意 URL。
  • AnyHttpUrl: 一个 HTTP URL。
  • HttpUrl: 更严格的 HTTP URL。
  • PostgresDsn: ostgres DSN 样式的 URL。
  • UUID1: 需要类型 1 的有效 UUID。
  • UUID3: 需要类型 3 的有效 UUID。
  • UUID4: 需要类型 4 的有效 UUID。
  • UUID5: 需要类型 5 的有效 UUID。
  • SecretBytes: 值部分 (partially) 保密的字节。
  • SecretStr: 值部分 (partially) 保密的字符串。
  • IPvAnyAddress: 允许一个 IPv4Address 或一个 IPv6Address。
  • IPvAnyInterface: 允许一个 IPv4Interface 或一个 IPv6Interface。
  • IPvAnyNetwork: 允许一个 IPv4Network 或一个 IPv6Network。
  • NegativeFloat: 允许负的浮点数; 使用标准的 float 解析,然后检查该值是否小于 0;
  • NegativeInt: 允许负的整数; 使用标准的 int 解析,然后检查该值是否小于 0;
  • PositiveFloat: 允许正的浮点数; 使用标准的 float 解析,然后检查该值是否大于 0;
  • PosiviteInt: 允许正的整数; 使用标准的 int 解析,然后检查该值是否大于 0;
  • conbytes: 用于约束 bytes 的类型方法。
  • condecimal: 用于约束 Decimal 的类型方法。
  • confloat: 用于约束 float 的类型方法。
  • conint: 用于约束 int 的类型方法。
  • conlist: 用于约束 list 的类型方法。
  • conset: 用于约束 set 的类型方法。
  • constr: 用于约束 str 的类型方法。

typing

typing:【标准库】类型标注支持

  • 作用:
    • 类型检查,防止运行时出现参数和返回值类型不符合
    • 作为开发文档附加说明,方便使用者调用时传入和返回参数类型
    • 模块加入后并不会影响程序的运行,不会报正式的错误,只有提醒
    • IDE 代码提示

使用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 函数类型注解
def _add(a: int, b: int) -> int:
return a + b

# 变量类型注解
name: str = "Li"
age: int = 18
items: list[int] = [1, 2, 3]
# 类型别名
from typing import TypeAlias, List

# Python 3.11前直接赋值创建类型别名
vector = List[float]

# Python 3.11 后使用TypeAlias创建类型别名
Vector: TypeAlias = List[float]

类型

  • int, float, bool, str, bytes
  • Python 3.9+:标准类型直接用
    • list:允许 list,tuple,set,frozenset,deque, 或生成器并转换为列表
    • tuple:允许 list,tuple,set,frozenset,deque, 或生成器并转换为元组
    • dict:字典类型
    • set:允许 list,tuple,set,frozenset,deque, 或生成器和转换为集合;
    • frozenset:允许 list,tuple,set,frozenset,deque, 或生成器和强制转换为冻结集
    • deque:允许 list,tuple,set,frozenset,deque, 或生成器和强制转换为双端队列
  • Python 3.8
    • Any:允许任何类型,包括 None,因此一个 Any 字段是可选的。
    • TypeVar:根据 constraints 或 bound 约束允许的值。
    • Union[int,str]:可接受多种类型的任意一种
      • 比如:list[Union[int, str]] = [3, 5, "test", "fun"]
    • Optional:可以是给定类型的值或者是None。对于可能为空的返回值或参数非常有用
      • Optional[x]Union[x, None] 的快捷方式
    • List[int]
      • List[int] = [1]
    • Tuple[int,str,float]=(3, "yes", 7.5)
      • Tuple[int, ...] = (1, 2, 3)
    • typing.Dict
      • Dict[str, float] = {"field": 2.0}
    • Set
      • Set[int] = {6, 7}
    • FrozenSet
    • Deque
    • Sequence
    • Iterable:这是为不应该被消耗的可迭代对象保留的。
    • Listeral:限定变量只能取某些特定的字面量,常用于配置选项、枚举值
    • TypeAlias:为复杂类型创建别名
    • Type
    • Callable:可调用对象(比如函数、方法或类)
      • 可带输出: Callable[[int], str]
    • Pattern:将会导致输入值被传递到 re.compile(v) 以创建一个正则表达式模式。
  • None, type(None) 或 Literal[None]:只允许 None 值
  • TypeVar:泛型类:类或方法时指定某些参数或返回值的类型可以是任意类型,但是调用者必须提供一个具体的类型来替代这个类型变量。TypeVar 主要用于定义泛型函数、类和容器。
1
2
3
4
5
6
7
from typing import TypeVar, List

T = TypeVar('T') # 创建一个名为 T 的类型变量
# 在这里,传入一个 List[int],那么返回值也将是 int 类型;如果你传入 List[str],则返回值将是 str 类型
def first_element(lst: List[T]) -> T:
"""Return the first element of a non-empty list."""
return lst[0] if lst else None
  • NewType:创建名义上不同的类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from typing import NewType

UserId = NewType('UserId', int)

def get_user_name(user_id: UserId) -> str:
# 在这里实现获取用户名的逻辑
return f"User {user_id}"

# 正确使用
user_id = UserId(42)
print(get_user_name(user_id))

# 错误使用(静态分析会捕捉到这个错误)
# print(get_user_name(42)) # 类型检查器将报告错误

# 在这个例子中,UserId 被定义为 int 的一种特殊形式。然而,在静态类型检查期间,UserId 和 int 将被视为不同的类型。因此,直接传递一个整数给 get_user_name 函数会导致类型检查错误,而传递一个 UserId 对象则是正确的。
  • object:对象
  • datetime 的 date,datetime,time,timedelta 等日期类型
  • FilePath,文件路径
  • DirectoryPath 目录路径
  • EmailStr 电子邮件地址
  • NameEmail 有效的电子邮件地址或格式
  • PyObject 需要一个字符串并加载可在该虚线路径中导入的 python 对象;
  • Color 颜色类型
  • AnyUrl 任意网址
  • SecretStr、SecretBytes 敏感信息,将被格式化为 '**********'''
  • Json 类型
  • PaymentCardNumber 支付卡类型
  • Enum
  • 约束类型,可以使用 con* 类型函数限制许多常见类型的值
  • list[str]:字符串数组
  • tuple[int,int]:2 个整数元素的元组
  • tuple[int,…]:任意数量整数元素的元组
  • dict[str,int]:key 为字符串,value 是整数的字典
  • Iterable[int]:可迭代类型,元素为整数
  • Sequence[bool]:布尔值序列
  • Mapping[str,int]:key 是字符串,value 是整数的映射
  • Class:使用自 定上课铃类做为类型注释
  • Callable:是否可调用
  • NamedTuple
  • TypeVar:任意类型
  • Generator:生成器类型
  • NewType:声明一个不同的类型而又不实际执行创建新类型,在运行时,将返回一个仅返回其参数的虚拟函数
  • overload:给同一个函数多个类型来更准确地描述函数的行为
  • Literal:表明一个表达式等于某个特定的原始值。例如,如果我们用 type 注释一个变量 Literal["foo"],mypy 将理解该变量不仅是 typestr,而且还特别等于 string"foo"
  • Final:限定符来指示不应重新分配、重新定义或覆盖名称或属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

from typing improt Literal, Union, Optional
# 局部变量的标注在运行时不可访问,但全局变量、类属性和函数的标注会分别存放模块、类和函数的 __annotations__ 特殊属性中。
def func_test(name:str,age:int)-> str:
print(type(name))
print(f'My name is {name},age is {age}')

print(func_test.__annotations__)
typing.get_type_hints()

内置类型
x: int = 1
x: float = 1.0
x: bool = True
x: str = "test"
x: bytes = b"test"

child: bool
if age < 18:
child = True
else:
child = False

# 普通函数
def stringify(num: int) -> str:
return str(num)

# 生成器
def f(n: int) -> Iterable[int]:
i = 0
while i < n:
yield i
i += 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from typing import List,Set,Tuple

from typing import Optional

def get_student_info(info:List[str,int,float],teacher: Dict[str, float]):
name=info[0]
age=info[1]
height=info[2]

def say_hi(name: Optional[str] = None):
if name is not None:
print(f"Hey {name}!")
else:
print("Hello World")

TYPE_CHECKING

1
2
3
4
5
from typing import TYPE_CHECKING
# 仅用于编辑器提示用,实际不会去导入
if TYPE_CHECKING:
    from ..manager import PageManager
   
给博主来一杯卡布奇诺