一种备选方案(可能不会像pydantic那样受欢迎)是使用除pydantic之外的反序列化库。例如,Dataclass Wizard库就支持这种特定用例。如果您需要与
Field(alias=...)
提供的相同往返行为,则可以将
all
参数传递给
json_field
函数。请注意,使用此类库,您会失去执行完整类型验证的能力,这可以说是pydantic最大的优点之一;但是,它确实以类似于pydantic的方式执行类型转换。我认为数据验证不太重要的原因如下。
- 如果您正在自己构建和传递输入,则可以相信您知道自己在做什么,并且正在传递正确的数据类型。
- 如果您从另一个API获取输入,则假设该API有良好的文档,您只需从其文档中获取示例响应,并使用该响应来模拟类结构。如果API清晰地记录其响应结构,则通常不需要进行任何验证。
- 数据验证需要时间,因此与仅执行类型转换并捕获可能发生的任何错误而不事先验证输入类型相比,它可能会稍微减慢过程。
因此,为了演示上述用例,这里是一个简单的示例,使用dataclass-wizard库(该库依赖于dataclasses
而不是pydantic模型):
from dataclasses import dataclass
from dataclass_wizard import JSONWizard, json_field
@dataclass
class TMDB_Category:
name: str = json_field('strCategory')
description: str = json_field('strCategoryDescription')
@dataclass
class TMDB_GetCategoriesResponse(JSONWizard):
categories: list[TMDB_Category]
运行该代码的方式如下:
input_dict = {
"categories": [
{
"strCategory": "Beef",
"strCategoryDescription": "Beef is ..."
},
{
"strCategory": "Chicken",
"strCategoryDescription": "Chicken is ..."
}
]
}
c = TMDB_GetCategoriesResponse.from_dict(input_dict)
print(repr(c))
print(c.to_dict())
性能测试
如果有人感兴趣,我已经设置了一个快速基准测试,以比较使用pydantic和仅使用dataclasses进行反序列化和序列化的时间:
from dataclasses import dataclass
from timeit import timeit
from pydantic import BaseModel, Field
from dataclass_wizard import JSONWizard, json_field
class Pydantic_TMDB_Category(BaseModel):
name: str = Field(alias="strCategory")
description: str = Field(alias="strCategoryDescription")
class Pydantic_TMDB_GetCategoriesResponse(BaseModel):
categories: list[Pydantic_TMDB_Category]
@dataclass
class TMDB_Category:
name: str = json_field('strCategory', all=True)
description: str = json_field('strCategoryDescription', all=True)
@dataclass
class TMDB_GetCategoriesResponse(JSONWizard):
categories: list[TMDB_Category]
input_dict = {
"categories": [
{
"strCategory": f"Beef {i * 2}",
"strCategoryDescription": "Beef is ..." * i
}
for i in range(100)
]
}
n = 10_000
print('=== LOAD (deserialize)')
print('dataclass-wizard: ',
timeit('c = TMDB_GetCategoriesResponse.from_dict(input_dict)',
globals=globals(), number=n))
print('pydantic: ',
timeit('c = Pydantic_TMDB_GetCategoriesResponse.parse_obj(input_dict)',
globals=globals(), number=n))
c = TMDB_GetCategoriesResponse.from_dict(input_dict)
pydantic_c = Pydantic_TMDB_GetCategoriesResponse.parse_obj(input_dict)
print('=== DUMP (serialize)')
print('dataclass-wizard: ',
timeit('c.to_dict()',
globals=globals(), number=n))
print('pydantic: ',
timeit('pydantic_c.dict()',
globals=globals(), number=n))
以下是基准测试结果(在Mac OS Big Sur,Python 3.9.0上进行测试):
=== LOAD (deserialize)
dataclass-wizard: 1.742989194
pydantic: 5.31538175
=== DUMP (serialize)
dataclass-wizard: 2.300118940
pydantic: 5.582638598
在他们的文档中,
pydantic
声称是最快的库,但很容易证明不是这样。如上所述,对于上述数据集,
pydantic
在反序列化和序列化过程中都慢了约2倍。值得注意的是,
pydantic
已经非常快了。
< p > < em > 免责声明 :我是该库的创建者(和维护者)。< /p >
.dict()
和.json()
中未使用别名。根据文档,它们是否被使用取决于by_alias
布尔关键字参数。考虑到作者认为别名是“在您的应用程序中公开使用的字段名称与在JavaScript、API、解析文件等地方使用的名称之间的映射”,这个默认值确实很奇怪。 - bluenote10response_model_by_alias=False
,正如这个答案中提到的那样:https://dev59.com/q8Lra4cB1Zd3GeqPLIEG#69679104 - Garrett Motzner