В статье автор шаг за шагом пересобирает упрощённый
@dataclass, показывая ключевые идеи.➡️ Dataclass не оборачивает класс
В отличие от обычных декораторов, он:
— читает метаданные класса
— добавляет методы прямо в этот же класс
— возвращает тот же объект
def dataclass(cls):
# модифицируем cls
return cls
➡️ Поля берутся из
__annotations__Все поля dataclass — это просто type hints:
class User:
name: str
age: int
print(User.__annotations__)
# {'name': str, 'age': int}
Именно этот словарь используется для генерации
__init__, __repr__ и других методов.➡️ Генерация методов через
execОдна из самых неочевидных частей:
dataclass генерирует код как строки и выполняет его через exec.Пример простейшего
__init__, созданного динамически:
def __init__(self, name, age):
self.name = name
self.age = age
Этот код сначала собирается как строка, затем превращается в функцию и добавляется в класс через
setattr.➡️ Как работает
frozen=TrueЧтобы сделать объект неизменяемым:
— переопределяются
__setattr__ и __delattr__— любые попытки изменить поле → ошибка
def __setattr__(self, name, value):
raise Exception("Cannot assign to frozen dataclass")
Но есть нюанс:
__init__ тоже использует __setattr__. Поэтому внутри
__init__ приходится писать так:
object.__setattr__(self, "age", age)
➡️ Автогенерация
__repr____repr__ строится из self.__dict__:
def __repr__(self):
return f"User(name={self.name!r}, age={self.age!r})"
Флаг
!r гарантирует, что значения форматируются через их __repr__, а не __str__.➡️ Итоговая минимальная версия dataclass
В итоге кастомный
@dataclass:— читает
__annotations__— через
exec создаёт __init__, __repr__— опционально блокирует изменения (`frozen`)
— не создаёт новых объектов, а модифицирует существующий класс
⚠️ Отдельно отмечается: если нужны валидации типов — лучше смотреть в сторону Pydantic, который использует те же аннотации, но идёт дальше.
📖 Полный разбор — по ссылке.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека питониста
#буст