pydantic:在将其分配到字段之前转换值?

发布于 2025-02-01 04:32:11 字数 531 浏览 3 评论 0原文

我有以下模型

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

,我想这样实例化:

fields = {'size': '1920x1080'}
window = Window(**fields)

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

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

Pydantic支持这一点吗?

I have the following model

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

and I would like to instantiate it like this:

fields = {'size': '1920x1080'}
window = Window(**fields)

Of course this fails since the value of 'size' is not of the correct type. However, I would like to add logic so that the value is split at x, i.e.:

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

Does Pydantic support this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

比忠 2025-02-08 04:32:11

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)

注意:

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

Pydantic 1.x(原始答案)

您可以使用Pydantic的验证器实现这种行为。给定预定义的函数:

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

您可以在类中实现它:

from pydantic import BaseModel, validator


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

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


现在:

fields = {'size': '1920x1080'}
window = Window(**fields)
print(window)
# output: size=(1920, 1080)

请注意,在那之后,您将无法用元组来实例化窗口

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

为了克服这一点,如果通过稍微更改代码通过元组,您可以简单地绕过该功能:

pydantic 2.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)

pydantic 1.x 1.x

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)

应该给出:

fields2 = {'size': (800, 600)}
window2 = Window(**fields2)
print(window2)
# output: size:(800, 600)

Pydantic 2.x (edit)

Pydantic 2.0 introduced the field_validator decorator which lets you implement such a behaviour in a very simple way. Given the original parsing function:

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)

Note:

  • The validator method is a class method, as denoted by the cls first argument. Implementing it as an instance method (with self) will raise an error.
  • The mode="before" in the decorator is critical here, as expected this is what makes the method run before checking "size" is a tuple.

Pydantic 1.x (original answer)

You can implement such a behaviour with pydantic's validator. Given your predefined function:

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

You can implement it in your class like this:

from pydantic import BaseModel, validator


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

Note the pre=True argument passed to the validator. It means that it will be run before the default validator that checks if size is a tuple.


Now:

fields = {'size': '1920x1080'}
window = Window(**fields)
print(window)
# output: size=(1920, 1080)

Note that after that, you won't be able to instantiate your Window with a tuple for size.

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

In order to overcome that, you could simply bypass the function if a tuple is passed by altering slightly your code:

Pydantic 2.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)

Pydantic 1.x

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)

Which should give:

fields2 = {'size': (800, 600)}
window2 = Window(**fields2)
print(window2)
# output: size:(800, 600)
你是暖光i 2025-02-08 04:32:11

如果您想将转换/验证逻辑保持更模块化或与类本身分离,还有另一个选择。

from pydantic import BaseModel, AfterValidator
from typing_extensions import Annotated

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

WindowSize = Annotated[str, AfterValidator(transform)]

class Window(BaseModel):
    size: WindowSize

fields = {'size': '1920x1080'}
window = Window(**fields)

print(window.size)  # (1920, 1080)

There is another option if you would like to keep the transform/validation logic more modular or separated from the class itself.

from pydantic import BaseModel, AfterValidator
from typing_extensions import Annotated

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

WindowSize = Annotated[str, AfterValidator(transform)]

class Window(BaseModel):
    size: WindowSize

fields = {'size': '1920x1080'}
window = Window(**fields)

print(window.size)  # (1920, 1080)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文