Перейти к основному содержимому
Модуль 9 · Урок 2 из 434/36 в курсе~12 min
Уроки модуля (2/4)

dataclass: классы данных без шаблона

Декоратор @dataclass (модуль dataclasses) автоматически генерирует __init__, __repr__ и __eq__ для классов, единственная задача которых — удерживать данные. Меньше шаблонов, более понятный код.

Без класса данных (шаблон)

Python
class Punto:
    def __init__(self, x: float, y: float) -> None:
        self.x = x
        self.y = y
    def __repr__(self) -> str:
        return f"Punto(x={self.x}, y={self.y})"
    def __eq__(self, other) -> bool:
        if not isinstance(other, Punto):
            return NotImplemented
        return (self.x, self.y) == (other.x, other.y)

С классом данных

Python
from dataclasses import dataclass

@dataclass
class Punto:
    x: float
    y: float

p = Punto(3, 4)
p             # Punto(x=3, y=4)   ← __repr__ gratis
Punto(3, 4) == Punto(3, 4)        # True ← __eq__ gratis
p.x, p.y      # 3, 4

Шаблон: объявить поля как аннотации класса (name: type), опционально со значением по умолчанию. @dataclass генерирует остальное.

По умолчанию и default_factory

Python
from dataclasses import dataclass, field

@dataclass
class Articolo:
    nome: str
    prezzo: float = 0.0
    tag: list[str] = field(default_factory=list)

замороженный = Истина: неизменяемый

Python
@dataclass(frozen=True)
class Coordinata:
    lat: float
    lon: float

c = Coordinata(45.4, 9.2)
c.lat = 99   # FrozenInstanceError!

Классы данных frozen также хешируются: вы можете использовать их в качестве ключей dict. или установить элементы.

order=True: автоматическое сравнение

Python
@dataclass(order=True)
class Voto:
    valore: int

Voto(10) < Voto(20)   # True
sorted([Voto(30), Voto(10), Voto(20)])

Генерирует __lt__, __le__, __gt__, __ge__ сравнения полей в порядке они были объявлены.

Обычные методы

Классы данных остаются обычными классами: вы можете добавлять методы.

Python
import math
from dataclasses import dataclass

@dataclass
class Punto:
    x: float
    y: float
    def distanza_dall_origine(self) -> float:
        return math.hypot(self.x, self.y)

Punto(3, 4).distanza_dall_origine()   # 5.0

Неизменяемые и эффективные классы данных

Вы можете сделать класс данных неизменяемым, передав аргумент frozen=True декоратору: @dataclass(frozen=True). Это вызывает ошибку при любой попытке изменить атрибуты после создания экземпляра, что делает экземпляры безопасными для параллельных сред или для использования в качестве ключей словаря.

Попробуйте сами

Упражнение#python.m9.l2.e1
Попыток: 0Загрузка…

Создайте класс данных «Book» с полями «title: str» и «pages: int». Создайте его экземпляр как `l = Book('Moby Dick', 635)`. Оцените `l == Book('Моби Дик', 635)`.

Загрузка редактора…
Показать подсказку

@dataclass генерирует __eq__, сравнивая поле за полем.

Решение доступно после 3 попыток

Обзор упражнения

Упражнение#python.m9.l2.e2
Попыток: 0Загрузка…

Создайте класс данных Basket с полем item: list[str] и default_factory=list. Создайте экземпляры `c1 = Basket()` и `c2 = Basket()`, добавьте "mela" к c1.items. Оцените `(c1.items, c2.items)`, чтобы убедиться, что это НЕ один и тот же список.

Загрузка редактора…
Показать подсказку

field(default_factory=list) гарантирует создание нового списка для каждого экземпляра.

Решение доступно после 3 попыток

Дополнительная задача

Упражнение#python.m9.l2.e3
Попыток: 0Загрузка…

Import `dataclass` from `dataclasses`. Create a dataclass `Point` containing two float coordinates `x` and `y`. Instantiate a point with `x=1.5` and `y=2.5` storing it in `p`. Finally, evaluate `p`.

Загрузка редактора…
Показать подсказку

Используйте @dataclass над классом Point и объявите x: float и y: float.

Решение доступно после 3 попыток