@dataclass сам пишет __init__ — а где тогда проверять значения, как сеттер в модуле 3? И как сделать
объект, который нельзя изменить, чтобы он не «пропадал» из set? У декоратора есть настройки на эти случаи.
__post_init__: проверка после создания
Если в dataclass объявить метод __post_init__, сгенерированный __init__ вызовет его последним действием —
когда все поля уже заполнены. Это место для проверок и для приведения значений к нужному виду:
from dataclasses import dataclass
@dataclass
class Cup:
name: str
volume: int
def __post_init__(self): # вызывается в конце __init__
self.name = self.name.title()
if self.volume < 50:
raise ValueError(f"объём {self.volume} мл — меньше 50 мл не бывает")
print(Cup("капучино", 300))
try:
Cup("эспрессо", 30)
except ValueError as error:
print("ValueError:", error)Cup(name='Капучино', volume=300) ValueError: объём 30 мл — меньше 50 мл не бывает
Учтите: __post_init__ проверяет значения только при создании. Присваивание позже, cup.volume = 10,
пройдёт без проверки — сеттер из модуля 3 такое ловил. Если поле нельзя испортить и потом, сделайте экземпляр
неизменяемым (frozen=True, ниже) или верните свойство с сеттером.
Допишите dataclass Product (поля name и price): метод __post_init__ убирает пробелы по краям
названия, а если название после этого пустое или цена не больше нуля — выбрасывает ValueError.
Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
from dataclasses import dataclass
@dataclass
class Product:
name: str
price: int
def __post_init__(self):
...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
from dataclasses import dataclass
@dataclass
class Product:
name: str
price: int
def __post_init__(self):
self.name = self.name.strip()
if not self.name:
raise ValueError("пустое название")
if self.price <= 0:
raise ValueError("цена должна быть больше нуля")frozen=True: неизменяемый экземпляр
@dataclass(frozen=True) запрещает менять поля после создания: присваивание выбрасывает
FrozenInstanceError (это подкласс AttributeError). Зато такой экземпляр хешируемый. Обычный
dataclass — нет: он пишет __eq__ и, как в уроке о равенстве, отключает хеш. А с frozen=True dataclass
сам пишет __hash__ из полей, и объект можно класть в set и делать ключом dict — не боясь, что он
«потеряется» после изменения, как в уроке о равенстве. Запрет действует везде, даже в __post_init__:
self.name = self.name.strip() в неизменяемом классе выбросит FrozenInstanceError — такой класс только
проверяет поля, а не исправляет их.
@dataclass(frozen=True)
class Coupon:
code: str
percent: int
autumn = Coupon("ОСЕНЬ", 10)
try:
autumn.percent = 50
except AttributeError as error:
print(type(error).__name__, error)
used = {autumn, Coupon("ОСЕНЬ", 10), Coupon("ЗИМА", 15)}
print(len(used), Coupon("ОСЕНЬ", 10) in used)FrozenInstanceError cannot assign to field 'percent' 2 True
Объявите dataclass Money с одним полем rubles (целое), неизменяемый. Проверьте себя: экземпляр не даёт
менять rubles и годится как ключ словаря.
Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
from dataclasses import dataclass
class Money:
...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
from dataclasses import dataclass
@dataclass(frozen=True)
class Money:
rubles: intreplace: изменённая копия
Как «поменять» цену неизменяемого товара? Создать новый экземпляр с другой ценой. Функция replace из модуля
dataclasses делает это коротко: копирует все поля, кроме названных:
from dataclasses import replace
winter = replace(autumn, code="ЗИМА", percent=15)
print(autumn)
print(winter)Coupon(code='ОСЕНЬ', percent=10) Coupon(code='ЗИМА', percent=15)
order=True: сравнение и сортировка
@dataclass(order=True) пишет __lt__, __le__, __gt__ и __ge__: экземпляры сравниваются как кортежи
своих полей — по первому полю, при равенстве — по второму и так далее. Поэтому порядок полей становится
важен: сначала поле, по которому сортируют.
Объявите dataclass Booking с полями time (строка «ЧЧ:ММ») и name (имя гостя) так, чтобы брони
сортировались по времени, а при одинаковом времени — по имени. Затем сохраните в ordered список имён из
броней bookings, отсортированных по порядку.
sorted и соберите имена генератором списка.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
from dataclasses import dataclass
class Booking:
...
bookings = [Booking("18:30", "Вера"), Booking("09:30", "Борис"), Booking("18:30", "Анна")]
ordered = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
from dataclasses import dataclass
@dataclass(order=True)
class Booking:
time: str
name: str
bookings = [Booking("18:30", "Вера"), Booking("09:30", "Борис"), Booking("18:30", "Анна")]
ordered = [b.name for b in sorted(bookings)]Товары menu — неизменяемые (Price из заготовки). Сохраните в new_menu новый список, где у каждого товара
цена выше на 10 %, округлённая до целых. Исходный список и его товары не меняются.
replace с новой ценой; округлите её round.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
from dataclasses import dataclass, replace
@dataclass(frozen=True)
class Price:
name: str
price: int
menu = [Price("Латте", 220), Price("Чай", 120), Price("Какао", 190), Price("Маффин", 133)]
new_menu = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
from dataclasses import dataclass, replace
@dataclass(frozen=True)
class Price:
name: str
price: int
menu = [Price("Латте", 220), Price("Чай", 120), Price("Какао", 190), Price("Маффин", 133)]
new_menu = [replace(p, price=round(p.price * 110 / 100)) for p in menu]Что верно для @dataclass(frozen=True)?
frozen=True запрещает любое присваивание полям, даже из методов. Именно поэтому экземпляр хешируемый: его хеш никогда не изменится. «Изменить» его можно только созданием нового объекта — replace делает это за одну строку.