Перейти до основного вмісту
Модуль 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 і default_factory

Python
from dataclasses import dataclass, field

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

frozen=True: незмінний

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('Moby Dick', 635)`.

Завантаження редактора…
Показати підказку

@dataclass генерує __eq__, порівнюючи поле за полем.

Рішення доступне після 3 спроб

Повторна вправа

вправи#python.m9.l2.e2
Спроби: 0Завантаження…

Створіть клас даних `Кошик` з полем `items: 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 спроб