Python dataclass: поля, значения по умолчанию, в словарь

Категория: Python · Язык: Python · Обновлено:

Декоратор @dataclass из модуля dataclasses генерирует для класса __init__, __repr__ и __eq__ по аннотациям полей. Не нужно писать шаблонный код: достаточно перечислить поля с типами. Преобразовать объект в словарь можно функцией asdict.

Базовый пример

Python
from dataclasses import dataclass, field, asdict, astuple, replace

@dataclass
class User:
    name: str
    age: int = 18
    tags: list[str] = field(default_factory=list)

user = User("Анна", 31)
print(user)                      # User(name='Анна', age=31, tags=[])
print(user == User("Анна", 31))  # True
print(asdict(user))              # {'name': 'Анна', 'age': 31, 'tags': []}
print(astuple(user))             # ('Анна', 31, [])

older = replace(user, age=32)
print(older.age)                 # 32
print(user.age)                  # 31

Функция replace создаёт копию с изменёнными полями, исходный объект остаётся прежним.

Изменяемые значения по умолчанию

Список, словарь и множество нельзя задавать как значение по умолчанию напрямую: dataclass отклонит такой класс.

Python
try:
    @dataclass
    class Broken:
        tags: list = []
except ValueError as error:
    print(error)   # mutable default <class 'list'> for field tags is not allowed: use default_factory

Правильно писать field(default_factory=list), как в примере выше: для каждого объекта создаётся свой список.

Неизменяемые объекты и хеш

Python
@dataclass(frozen=True)
class Point:
    x: float
    y: float

p = Point(1, 2)
try:
    p.x = 5
except Exception as error:
    print(type(error).__name__)          # FrozenInstanceError

print(hash(p) == hash(Point(1, 2)))      # True
print({p: "метка"}[Point(1, 2)])         # метка

Объект с frozen=True можно использовать как ключ словаря и элемент множества.

Сравнение и сортировка

Python
@dataclass(order=True)
class Version:
    major: int
    minor: int

print(Version(1, 2) < Version(1, 10))                     # True
print(sorted([Version(2, 0), Version(1, 5)])[0])          # Version(major=1, minor=5)

Проверка и вычисляемые поля

Python
@dataclass
class Rectangle:
    width: float
    height: float
    area: float = field(init=False)

    def __post_init__(self):
        if self.width <= 0 or self.height <= 0:
            raise ValueError("стороны должны быть положительными")
        self.area = self.width * self.height

print(Rectangle(3, 4).area)   # 12

Метод __post_init__ вызывается сразу после автоматически созданного __init__.

Экономия памяти: slots

Python
@dataclass(slots=True)
class Light:
    value: int

item = Light(1)
print(hasattr(item, "__dict__"))   # False

Параметр slots=True доступен с Python 3.10. Объекты занимают меньше памяти, но в них нельзя добавлять новые атрибуты.

Dataclass, namedtuple и pydantic

dataclass

namedtuple

pydantic

Изменяемость

Да, или frozen=True

Нет

Настраивается

Проверка типов

Нет

Нет

Да

Встроенная в Python

Да

Да

Нет, отдельная библиотека

Частые вопросы

Проверяет ли dataclass типы полей?

Нет, аннотации служат подсказкой для людей и инструментов. Для проверки данных используйте __post_init__ или библиотеку pydantic.

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

Распакуйте словарь: User(**data). Для вложенных структур нужна ручная сборка или специальные библиотеки.