Тест самоперевірки

Dataclass і сучасний стиль

Обери відповідь — і одразу побачиш пояснення. Помилитись тут корисніше, ніж на іспиті.

← до лекції
Відповіді0 / 8   правильних: 0
Питання 1 / 8

Ти пишеш @dataclass над класом і оголошуєш поле рядком «кількість = 0» — без двокрапки й без типу. Що станеться?

Чому так. Декоратор бере поля саме з анотацій (Клас.__annotations__). Рядок без двокрапки для нього невидимий — залишається звичайний атрибут класу, спільний на всі примірники. Помилки не буде, і саме це найнеприємніше: Продаж(кількість=5) впаде з TypeError зовсім в іншому місці.
Питання 2 / 8

У класі під @dataclass написано «теги: list = []». Коли ти дізнаєшся, що це погана ідея?

Чому так. Декоратор упізнає list, dict і set у замовчуванні й відмовляється створювати клас. У темі 15 та сама пастка у функції def додати(товар, кошик=[]) не викликала жодного заперечення й тихо накопичувала значення між викликами — у цьому й уся різниця. Помилка, яка кричить одразу, дешевша за помилку, яка мовчить.
Питання 3 / 8

Клас під простим @dataclass, полів два, обидва порівнюються. Чому «{продаж}» падає з TypeError: unhashable type?

Чому так. Рівні обʼєкти зобовʼязані мати однаковий хеш. Якщо поля можна міняти, хеш «поїхав» би після першої ж зміни й обʼєкт загубився б у власній множині — тому мова радше забирає хеш, ніж дає зіпсований. Так само поводиться будь-який клас, де ти написав __eq__ руками. Повертає хеш frozen=True.
Питання 4 / 8

Клас оголошено як @dataclass(frozen=True), серед полів є «теги: list». Що зробить рядок p.теги.append("акція")?

Чому так. frozen дописує __setattr__, який забороняє перепризначити поле — переклеїти ярлик на інший обʼєкт. Список за цим ярликом лишається звичайним змінюваним списком із теми 06, і append нікого не питає. Справжня незмінність починається тоді, коли незмінні всі поля: заміни список кортежем.
Питання 5 / 8

У класі є поле «сума: float = field(init=False)». Звідки візьметься його значення?

Чому так. init=False прибирає поле із сигнатури __init__, тому передати його ззовні неможливо — і в цьому сенс: обчислюване значення не має приймати чужі дані. Заповнюють його в __post_init__, який виконується після того, як усі звичайні поля вже записані, тож у ньому доступне будь-яке з них.
Питання 6 / 8

У __post_init__ спрацював raise ValueError. Що станеться з обʼєктом, у якому на цей момент уже записані всі пʼять полів?

Чому так. __post_init__ виконується всередині __init__, тож виняток з нього вилітає прямо з конструктора. Імʼя ні до чого не привʼязується, і напівживий обʼєкт просто нікому не дістається. Саме тому перевірки ставлять сюди, а не в окремий метод, який хтось може забути покликати.
Питання 7 / 8

Виміряно: звичайний dataclass із пʼятьма полями — 104 байти на обʼєкт, зі slots=True — 72. У програмі таких обʼєктів десять тисяч. Що робити?

Чому так. 10 000 × 32 байти — це близько 0.3 МБ, менше за одну вкладку браузера. Натомість клас утратить __dict__, а з ним — можливість додати поле на льоту й частину бібліотек, що на цей словник розраховують. slots=True — інструмент для мільйонів однотипних записів, а не звичка на кожен клас: спершу зміряй, потім міняй.
Питання 8 / 8

Потрібен тип для запису «код валюти + курс»: поля відомі наперед, обʼєкт має бути незмінним і лягати в множину, жодних перевірок і методів. Що дешевше?

Чому так. Для простих незмінних даних без поведінки NamedTuple дешевший: він незмінний за визначенням, хешується, займає менше памʼяті й розпаковується як кортеж (код, курс = запис). dataclass виграє тоді, коли треба перевіряти дані при створенні, міняти поля після створення або тримати помітну поведінку — тут нічого з цього немає.