Python dataclass: поля, значения по умолчанию, в словарь
Декоратор @dataclass из модуля dataclasses генерирует для класса __init__, __repr__ и __eq__ по аннотациям полей. Не нужно писать шаблонный код: достаточно перечислить поля с типами. Преобразовать объект в словарь можно функцией asdict.
Базовый пример
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 отклонит такой класс.
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), как в примере выше: для каждого объекта создаётся свой список.
Неизменяемые объекты и хеш
@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 можно использовать как ключ словаря и элемент множества.
Сравнение и сортировка
@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)Проверка и вычисляемые поля
@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
@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 | |
|---|---|---|---|
Изменяемость | Да, или | Нет | Настраивается |
Проверка типов | Нет | Нет | Да |
Встроенная в Python | Да | Да | Нет, отдельная библиотека |
Частые вопросы
Проверяет ли dataclass типы полей?
Нет, аннотации служат подсказкой для людей и инструментов. Для проверки данных используйте __post_init__ или библиотеку pydantic.
Как превратить словарь обратно в dataclass?
Распакуйте словарь: User(**data). Для вложенных структур нужна ручная сборка или специальные библиотеки.