Нашли ошибку?
Скачать чистый
урок 1 из 4≈ 20 минупражнения: 0 из 3

dataclasses

Декоратор @dataclass пишет __init__, __repr__ и __eq__ за вас по списку полей с аннотациями. Значения по умолчанию и field(default_factory=list) для списков.

Python запустится при первом выполнении

Посмотрите на классы из прошлых модулей: Product, Money, Customer. В каждом — одинаковый __init__, который переписывает параметры в атрибуты, __repr__, который их перечисляет, и __eq__, который их сравнивает. Это много однообразного кода, в котором легко ошибиться: добавили атрибут — и забыли его в __eq__. Для таких классов данных (data class) в стандартной библиотеке есть модуль dataclasses.

@dataclass

Декоратор @dataclass читает список полей (field) класса и сам пишет __init__, __repr__ и __eq__. Поле объявляется в теле класса строкой имя: тип — это аннотация типа (type annotation): подсказка, какого типа значение ожидается.

[ ]Product на dataclass
from dataclasses import dataclass


@dataclass
class Product:
    name: str
    price: int


latte = Product("Латте", 220)
print(latte)
print(latte == Product("Латте", 220), latte == Product("Латте", 250))
print(latte.name, latte.price)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
Product(name='Латте', price=220)
True False
Латте 220

Три строки полей заменили собой три метода. __repr__ перечисляет поля с именами, __eq__ сравнивает все поля по порядку.

[ ]Упражнение 1Напиток-dataclass решено

Объявите dataclass Drink с полями name (строка), price (целое) и volume (целое). Свой __init__ не пишите — его создаст декоратор.

from dataclasses import dataclass


class Drink:
    ...

Значения по умолчанию

Значение по умолчанию задают прямо в поле: volume: int = 250. Поля со значением по умолчанию идут после полей без него — как параметры функции:

[ ]Объём по умолчанию
@dataclass
class Cup:
    name: str
    volume: int = 250


print(Cup("Капучино"), Cup("Капучино", 400))
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
Cup(name='Капучино', volume=250) Cup(name='Капучино', volume=400)

Методы и свойства в dataclass объявляются как обычно — декоратор их не трогает:

[ ]Упражнение 2Строка заказа решено

Объявите dataclass OrderLine с полями product (строка), price (целое) и qty (целое, по умолчанию 1) и свойством cost — стоимость строки, цена на количество.

class OrderLine:
    ...

Списки в полях: default_factory

Помните изменяемое значение по умолчанию из модуля 2? dataclass не даёт на него наступить — поле items: list = [] вызывает ошибку сразу при объявлении класса:

[ ]Список по умолчанию запрещёношибка: ValueError
@dataclass
class Basket:
    customer: str
    items: list = []
Ошибкасохранённый · нажмите «Выполнить», чтобы получить свой
ValueError: mutable default <class 'list'> for field items is not allowed: use default_factory

Нужно сказать, как создавать значение для каждого нового экземпляра: field(default_factory=list) — вызвать list() и получить новый пустой список. field импортируют из того же модуля:

[ ]Свой список у каждой корзины
from dataclasses import dataclass, field


@dataclass
class Basket:
    customer: str
    items: list = field(default_factory=list)


anna, boris = Basket("Анна"), Basket("Борис")
anna.items.append("Латте")
print(anna)
print(boris)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
Basket(customer='Анна', items=['Латте'])
Basket(customer='Борис', items=[])
[ ]Упражнение 3Покупатель с историей решено

Перепишите класс Customer из заготовки на dataclass: поля name, phone, points (по умолчанию 0) и visits — список дат визитов, у каждого покупателя свой, по умолчанию пустой. Метод visit(date) добавляет дату в visits и начисляет 10 баллов — его перенесите без изменений.

class Customer:
    def __init__(self, name, phone, points=0, visits=None):
        self.name = name
        self.phone = phone
        self.points = points
        self.visits = [] if visits is None else visits

    def __repr__(self):
        return f"Customer(name={self.name!r}, phone={self.phone!r}, points={self.points!r}, visits={self.visits!r})"

    def __eq__(self, other):
        if not isinstance(other, Customer):
            return NotImplemented
        return (self.name, self.phone, self.points, self.visits) == (other.name, other.phone, other.points, other.visits)

    def visit(self, date):
        self.visits.append(date)
        self.points += 10

Нашли ошибку?