Принудительное преобразование типа в методе __init__ python dataclass

У меня есть следующий очень простой класс данных:

import dataclasses

@dataclasses.dataclass
class Test:
    value: int

Я создаю экземпляр класса, но вместо целого использую строку:

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

На самом деле мне нужно принудительное преобразование в тип данных, который я определил в определении класса:

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

Придется ли мне писать метод __init__ вручную или есть простой способ добиться этого?


person johnson    schedule 25.02.2019    source источник


Ответы (4)


Подсказка типа атрибутов класса данных никогда не соблюдается в том смысле, что типы применяются или проверяются. Ожидается, что в основном средства проверки статического типа, такие как 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)

Для получения кортежа _ 4_, которые определяют поле и тип, и перебирают их, чтобы сделать это для каждое поле автоматически, не записывая его для каждого отдельно.

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))
person deceze♦    schedule 25.02.2019
comment
Благодарность! это не совсем то, что я хотел (исключение вместо преобразования), но благодаря вашему предложению я смог найти решение - person johnson; 25.02.2019
comment
Я бы ошибся на стороне исключений вместо неявного преобразования, но я дал вам альтернативу преобразования в комментариях там ... - person deceze♦; 25.02.2019
comment
упс, не видел последний комментарий! - person johnson; 25.02.2019
comment
Альтернативное решение с использованием декоратора вместо __post_init__. - person Jon Nalley; 05.03.2019

Этого легко добиться, используя 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)
person yanlunlu    schedule 03.02.2021
comment
Он находится в стадии бета-тестирования по состоянию на апрель 2021 года, но выглядит потрясающе! - person Noumenon; 06.04.2021

Вы можете добиться этого с помощью метода __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

person Merig    schedule 25.02.2019

Да, простой ответ - просто выполнить преобразование самостоятельно в своем собственном __init__(). Я делаю это, потому что мне нужны мои объекты frozen=True.

Что касается проверки типа, Pydandic утверждает, что это делает, но я еще не пробовал: https://pydantic-docs.helpmanual.io/

person Lars P    schedule 11.04.2019