在 python 数据类 __init__ 方法中强制类型转换

Force type conversion in python dataclass __init__ method

我有以下非常简单的数据class:

import dataclasses

@dataclasses.dataclass
class Test:
    value: int

我创建了一个 class 的实例,但我使用的是字符串而不是整数:

>>> test = Test('1')
>>> type(test.value)
<class 'str'>

我真正想要的是强制转换为我在 class 定义中定义的数据类型:

>>> test = Test('1')
>>> type(test.value)
<class 'int'>

我必须手动编写 __init__ 方法还是有简单的方法来实现?

从强制或检查类型的意义上讲,数据类属性的类型提示从不被遵守。大多数像 mypy 这样的静态类型检查器应该会做这个工作,Python 不会在运行时做,因为它从来没有做过。

如果要添加手动类型检查代码,请在 __post_init__ 方法中执行:

@dataclasses.dataclass
class Test:
    value: int

    def __post_init__(self):
        if not isinstance(self.value, int):
            raise ValueError('value not an int')
            # or self.value = int(self.value)

您可以使用 dataclasses.fields(self) to get a tuple of Field 指定字段和类型的对象并对其进行循环以自动为每个字段执行此操作,而无需为每个字段单独编写。

def __post_init__(self):
    for field in dataclasses.fields(self):
        value = getattr(self, field.name)
        if not isinstance(value, field.type):
            raise ValueError(f'Expected {field.name} to be {field.type}, '
                             f'got {repr(value)}')
            # or setattr(self, field.name, field.type(value))

您可以使用 __post_init__ 方法实现此目的:

import dataclasses

@dataclasses.dataclass
class Test:
    value : int

    def __post_init__(self):
        self.value = int(self.value)

此方法在 __init__ 方法之后调用

https://docs.python.org/3/library/dataclasses.html#post-init-processing

是的,简单的答案是自己在 __init__() 中自己进行转换。我这样做是因为我想要我的对象 frozen=True.

对于类型验证,Pydandic 声称可以做到,但我还没有尝试过:https://pydantic-docs.helpmanual.io/

使用pydantic.validate_arguments

很容易实现

只需在您的数据类中使用 validate_arguments 装饰器:

from dataclasses import dataclass
from pydantic import validate_arguments


@validate_arguments
@dataclass
class Test:
    value: int

然后试试你的演示,'str type' 1 将从 str 转换为 int

>>> test = Test('1')
>>> type(test.value)
<class 'int'>

如果你传递了真正错误的类型,它会抛出异常

>>> test = Test('apple')
Traceback (most recent call last):
...
pydantic.error_wrappers.ValidationError: 1 validation error for Test
value
  value is not a valid integer (type=type_error.integer)

使用 Python dataclasses, the alternative is to use the __post_init__ 方法,正如其他答案中指出的那样:

@dataclasses.dataclass
class Test:
    value: int

    def __post_init__(self):
        self.value = int(self.value)

或者您可以使用 attrs package, which allows you to easily set converters:

@attr.define
class Test:
    value: int = attr.field(converter=int)