Hic*_*idi 10 python annotations python-typing pydantic
创建Pydantictyping.Optional[]模型时隐式将可选属性设置为 None与显式分配之间有什么区别?在这两种情况下,属性最终都会在类对象实例化时具有一个值。typing.Optional[] = NoneNone
import typing
import pydantic
class Bar(pydantic.BaseModel):
a: typing.Optional[int]
@pydantic.validator('a', always=True, pre=True)
def check_a(cls, v, values, field):
print("WITHOUT NONE")
print(values)
print(field)
return v
class Baz(pydantic.BaseModel):
a: typing.Optional[int] = None
@pydantic.validator('a', always=True, pre=True)
def check_a(cls, v, values, field):
print("WITH NONE")
print(values)
print(field)
return v
print(Bar())
print(Baz())
Run Code Online (Sandbox Code Playgroud)
输出:
WITHOUT NONE
{}
name='a' type=Optional[int] required=False default=None
a=None
WITH NONE
{}
name='a' type=Optional[int] required=False default=None
a=None
Run Code Online (Sandbox Code Playgroud)
Dan*_*erg 10
没有区别。至少在功能方面是这样。隐式地将默认值设置为None(除非指定了另一个默认值)。另一个显式地将默认值设置为None。
您可能已经知道它Optional[T]相当于Union[T, None]or T | None(在新的表示法中)。在所有其他情况下,使用类型T并U简单地将字段注释为a: T | U而不提供显式默认值将使其成为必填字段。在 Pydantic 术语中,这意味着如果不(以某种方式)为该字段提供值,则无法实例化模型,并且该值必须是类型T或类型U才能通过验证。
并集T | None是该规则的一个例外,它(可以说)并不明显,因此您可以将其称为不一致。但这似乎是故意的。使用这样的联合注释字段(例如a: Optional[T])将始终创建一个不需要的字段并且具有默认值None(当然除非您指定其他默认值)。
在幕后,这种情况发生在模型创建期间,在ModelField实例几乎创建完毕并prepare调用其方法之后。它调用该_type_analysis方法来确定有关字段类型的更多详细信息。在识别出类型 origin 是union后,依次查看其类型参数,一旦确定其中之一是 theNoneType,则将该字段的required属性设置为False,并将其allow_none属性设置为True。然后,稍后,因为该字段仍然有一个未定义的default属性,所以该属性被设置为None。如果您显式定义默认值(None或某些实例T)或默认工厂,则显然会跳过最后一步。
实际上,这意味着对于以下模型,所有字段的行为都相同:
None当没有提供其他值时,所有这些都将收到其值,Optional[str]。from typing import Optional, Union
from pydantic import BaseModel, Field
class Model(BaseModel):
a: str | None
b: Union[str, None]
c: Optional[str]
d: Optional[str] = None
e: Optional[str] = Field(default=None)
f: Optional[str] = Field(default_factory=lambda: None)
g: str = None
obj = Model()
print(obj.json(indent=4))
Run Code Online (Sandbox Code Playgroud)
输出:
{
"a": null,
"b": null,
"c": null,
"d": null,
"e": null,
"f": null,
"g": null
}
Run Code Online (Sandbox Code Playgroud)
请注意,即使我们用 just 注释它, Even 也是以g将其类型设置为 be 的方式隐式处理的。您可以通过执行以下操作来验证这一点:Optional[str]str
for field in Model.__fields__.values():
print(repr(field))
Run Code Online (Sandbox Code Playgroud)
输出:
ModelField(name='a', type=Optional[str], required=False, default=None)
ModelField(name='b', type=Optional[str], required=False, default=None)
ModelField(name='c', type=Optional[str], required=False, default=None)
ModelField(name='d', type=Optional[str], required=False, default=None)
ModelField(name='e', type=Optional[str], required=False, default=None)
ModelField(name='f', type=Optional[str], required=False, default_factory='<function <lambda>>')
ModelField(name='g', type=Optional[str], required=False, default=None)
Run Code Online (Sandbox Code Playgroud)
顺便说一下,你还可以创建一个typeOptional[T] 和required的字段。为此,您只需将默认值设置为省略号...,例如,field: Optional[str] = ...可以使该字段接受None作为值,但仍然要求您在初始化期间提供一个值。(参见文档)
当然,这是比较主观的,但我建议遵循Python 的禅宗:
显式的比隐式的好。
特别是因为这种行为非常不一致且不明显,除非您已经熟悉 Pydantic,所以即使可以,也不应该省略默认值。只需要您再添加几个字符 = None到字段定义中,但对于您或其他阅读代码的人来说,它会变得更加清晰。
我也不建议从注释中省略。NoneType原因是一样的。上一个示例中的字段g可能工作得很好,但它的类型发生了隐式更改。
我的建议是field: Optional[str] = None,如果您只处理一种附加类型field: str | int | None,则执行 ;如果有更多类型,则执行 (或旧的Union表示法,如果您使用 Python <=3.9)。
虽然之前的答案对于 pydantic v1 是正确的,但请注意, 2023 年 6 月 30 日发布的pydantic v2更改了此行为。
如迁移指南中所述:
Pydantic V2 更改了一些用于指定是否需要注释为Optional的字段(即没有默认值)或不需要(即具有 None 或相应类型的任何其他值的默认值)的逻辑,现在更接近匹配数据类的行为。同样,注释为 Any 的字段不再具有默认值 None。
| 归档时间: |
|
| 查看次数: |
9188 次 |
| 最近记录: |