Pydantic:在将值分配给字段之前转换值?

tri*_*ous 9 python python-3.x pydantic

我有以下型号

class Window(BaseModel):
    size: tuple[int, int]
Run Code Online (Sandbox Code Playgroud)

我想像这样实例化它:

fields = {'size': '1920x1080'}
window = Window(**fields)
Run Code Online (Sandbox Code Playgroud)

当然,这会失败,因为 的值'size'不是正确的类型。但是,我想添加逻辑,以便该值在 处分割x,即:

def transform(raw: str) -> tuple[int, int]:
    x, y = raw.split('x')
    return int(x), int(y)
Run Code Online (Sandbox Code Playgroud)

Pydantic 支持这个吗?

ye *_*obe 13

Pydantic 2.x(编辑)

Pydantic 2.0 引入了field_validator装饰器,它可以让您以非常简单的方式实现这样的行为。给定原始解析函数:

from pydantic import BaseModel, field_validator

class Window(BaseModel):
    size: tuple[int, int]

    @field_validator("size", mode="before")
    @classmethod
    def transform(cls, raw: str) -> tuple[int, int]:
        x, y = raw.split("x")
        return int(x), int(y)
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 验证器方法是一个类方法,如第一个参数所示cls。将其实现为实例方法(使用self)会引发错误。
  • 装饰器中mode="before"的 在这里至关重要,正如预期的那样,这就是使方法在检查“size”是否为元组之前运行的原因。

Pydantic 1.x(原始答案)

您可以使用 pydantic 来实现这样的行为validator。给定您的预定义函数:

def transform(raw: str) -> tuple[int, int]:
    x, y = raw.split('x')
    return int(x), int(y)
Run Code Online (Sandbox Code Playgroud)

你可以在你的类中实现它,如下所示:

from pydantic import BaseModel, validator


class Window(BaseModel):
    
    size: tuple[int, int]
    _extract_size = validator('size', pre=True, allow_reuse=True)(transform)

Run Code Online (Sandbox Code Playgroud)

请注意pre=True传递给验证器的参数。这意味着它将在检查是否为元组的默认验证器之前运行。size


现在:

fields = {'size': '1920x1080'}
window = Window(**fields)
print(window)
# output: size=(1920, 1080)
Run Code Online (Sandbox Code Playgroud)

请注意,此后,您将无法使用Window大小元组实例化您的。

fields = {'size': '1920x1080'}
window = Window(**fields)
print(window)
# output: size=(1920, 1080)
Run Code Online (Sandbox Code Playgroud)

为了克服这个问题,如果通过稍微改变代码来传递元组,您可以简单地绕过该函数:

派丹提克 2.x

fields2 = {'size': (800, 600)}
window2 = Window(**fields2)
# AttributeError: 'tuple' object has no attribute 'split'
Run Code Online (Sandbox Code Playgroud)

派丹提克 1.x

class Window(BaseModel):
    size: tuple[int, int]

    @field_validator("size", mode="before")
    def transform(cls, raw: str | tuple[int, int]) -> tuple[int, int]:
        if isinstance(raw, tuple):
            return raw
        x, y = raw.split("x")
        return int(x), int(y)
Run Code Online (Sandbox Code Playgroud)

这应该给出:

def transform(raw: str | tuple[int, int]) -> tuple[int, int]:
    if isinstance(raw, tuple):
        return raw
    x, y = raw.split('x')
    return int(x), int(y)

class Window(BaseModel):

    size: tuple[int, int]
    _extract_size = validator('size', pre=True, allow_reuse=True)(transform)
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你!`pre=True` 关键字是缺失的部分...我想补充一点,如果 `allow_reuse ` 不需要此功能。 (2认同)