@albertalexandrov

Как преобразовать значение при использовании dataclass?

Перевожу данные в dataclass следующим образом:

from dataclasses import dataclass
from datetime import datetime, date


@dataclass
class Person:
    first_name: str
    last_name: str
    bdate: date

    def __post_init__(self):
        self.bdate = datetime.strptime(self.bdate, '%Y%m%d').date()


data = {
    'first_name': 'Adam', 
    'last_name': 'Smith', 
    'bdate': '20220617'
}
person = Person(**data)

print(person)


Возник вопрос с преобразованием bdate из строки в дату. В целом представленный вариант выше работает, но IDE подсвечивает:

6300ef14b2da5416171168.jpeg

В какой момент и в каком месте преобразовывать bdate так, чтобы IDE не ругалась?
  • Вопрос задан
  • 402 просмотра
Решения вопроса 1
Vindicar
@Vindicar
RTFM!
Я в таких случаях создаю фабричный метод.
@dataclass
class Person:
    first_name: str
    last_name: str
    bdate: date

    @classmethod
    def make(cls, first_name: str, last_name: str, bdate: str) -> 'Person':
        _bdate = datetime.strptime(bdate, '%Y%m%d').date()
        return cls(first_name=first_name, last_name=last_name, bdate=_bdate)

data = {
    'first_name': 'Adam', 
    'last_name': 'Smith', 
    'bdate': '20220617'
}
person = Person.make(**data)

Просто, коротко, позволяет реализовать любую логику приведения типов и вычисления значений по умолчанию, использует базовые механизмы Питона, при необходимости можно проигнорировать и использовать обычный конструктор (который мы не ломаем).

Но вообще это неправильное распределение обязанностей. Обязанность датакласса - хранить данные, а не менять их представление. За смену представления пусть отвечает тот код, который получает значение строки.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
drygdryg
@drygdryg
Python-разработчик
Возможно, вам стоит использовать Pydantic вместо dataclasses для этого: он позволяет создавать пользовательские валидаторы как отдельного поля, так и всех полей. Валидаторы можно использовать для преобразования типов данных. Что важно, он позволяет создавать пре-валидаторы, то есть те, которые будут применяться к полю перед стандартными валидаторами Pydantic. Например, вашу задачу можно решить так:
from datetime import date, datetime

from pydantic import BaseModel, validator


class Person(BaseModel):
    first_name: str
    last_name: str
    bdate: date

    @validator('bdate', pre=True)
    def bdate_from_string(cls, v):
        if isinstance(v, str):
            return datetime.strptime(v, '%Y%m%d').date()
        return v


data = {
    'first_name': 'Adam',
    'last_name': 'Smith',
    'bdate': '20220617'
}

person = Person(**data)
print(person)


Если вы не хотите использовать Pydantic, то можно посмотреть на продвинутую альтернативу dataclasses — attrs, может быть, там есть средства для решения вашей задачи.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы