Предметна область тут інша, ніж у лекції, — навмисно: якщо ти зможеш повторити ту саму
механіку на своїх даних, значить, справа не в завченому прикладі. Працюємо з
карткою читача бібліотеки. Усе роби в окремому файлі chytachi.py або в новому
зошиті — практику не редагуй, вона знадобиться для звірки.
рівень 1Рівень 1 — База
Опиши картку читача через @dataclass і доведи, що фабрика справді створює окремий
список кожному.
- Клас
Читачіз полями:імʼя(рядок),рік_народження(ціле),квиток(рядок),взяті_книги(список, порожній за замовчуванням). - Спершу напиши
взяті_книги: list = []і збережи текст помилки в коментарі — слово в слово. Потім полагодь черезfield(default_factory=list). - Створи двох читачів, додай книгу одному з них, надрукуй обох.
- Надрукуй
list(Читач.__dataclass_fields__)іreprодного читача.
Зроблено, якщо:
- у файлі є коментар із дослівним текстом
ValueErrorпроmutable default— і поруч робочий варіант ізdefault_factory; - проходить
assert перший.взяті_книги is not другий.взяті_книги; - проходить
assert другий.взяті_книги == []після того, як першому додали книгу; - проходить
assert list(Читач.__dataclass_fields__) == ["імʼя", "рік_народження", "квиток", "взяті_книги"]— тобто порядок полів саме той, що ти оголосив.
рівень 2Рівень 2 — Плюс
Додай до картки правила, яких декоратор згенерувати не може.
- Напиши
__post_init__, який кидаєValueError, якщо: імʼя порожнє або складається з пробілів;рік_народженняпоза межами 1900–2026. - Додай поле
вік: int = field(init=False)і обчисли його в тому ж__post_init__(вважай поточним 2026 рік). - Додай службове поле
останній_вхід: float = field(default=0.0, compare=False)— воно не має впливати на рівність двох карток. - Перевір, що поле
вікнеможливо передати в конструктор.
Зроблено, якщо:
- проходить
assert Читач("Олена", 1998, "Ч-17").вік == 28; - три
assert-и черезtry/exceptловлятьValueErrorдля трьох поганих випадків (порожнє імʼя, рік 1800, рік 2100) — і кожен перевіряє текст повідомлення, а не сам факт винятку; - проходить
assert Читач("Олена", 1998, "Ч-17", останній_вхід=9.0) == Читач("Олена", 1998, "Ч-17", останній_вхід=21.0)— тобтоcompare=Falseпрацює; - проходить
assert "вік" not in inspect.signature(Читач).parameters.
рівень 3Рівень 3 — Виклик
Дослідь межу незмінності й доведи її числами, а не словами.
- Зроби
ЗамороженийЧитаччерез@dataclass(frozen=True)з тими самими полями, девзяті_книги— список. Доведи двомаassert-ами: (а) поле не перепризначити, (б) список усередині все одно змінюється методомappend. - Покажи, що такий обʼєкт не хешується, і що падає при цьому не
Читач, аlist: зловиTypeErrorі перевір, що в тексті є словоlist. - Зроби другу версію, де
взяті_книги— кортеж. Доведи, що теперhash()працює, а дві однакові картки в множині зливаються в одну. - Зміряй
tracemalloc-ом памʼять на 200 000 карток для чотирьох варіантів: звичайний@dataclass,@dataclass(slots=True),NamedTupleі звичайний клас із__init__. Побудуй табличку «байтів на обʼєкт» і випиши висновок одним реченням. - Знайди й опиши випадок, коли
NamedTupleне підходить, аdataclassпідходить, і підкріпи його кодом на пʼять рядків.
Зроблено, якщо:
- є
assertна те, щоfrozenне захистив вкладений список — з коментарем, чому так; - є
assert "list" in текст_помилкидля випадку з нехешованим полем; - проходить
assert len({к1, к2}) == 1для двох однакових карток із кортежем; - у табличці памʼяті версія зі
slots=Trueменша за звичайну щонайменше на 20 %, і числа отримані виміром, а не переписані з лекції; - у пункті 5 названо конкретну можливість (наприклад, перевірка при створенні або
зміна поля після створення) і показано код, який на
NamedTupleне працює.
Підказки
- Рівень 1. Якщо помилка про
mutable defaultне зʼявилась — перевір, чи справді над класом стоїть@dataclass. Без декоратора це просто атрибут класу, і мовчазна пастка з теми 25 повертається. - Рівень 2.
field(init=False)без значення за замовчуванням цілком законний, поки__post_init__це поле заповнює. Але якщо перевірка кине виняток раніше за присвоєння, поля в обʼєкті так і не буде — це нормально, обʼєкт усе одно нікому не дістанеться. - Рівень 3. Замок стоїть на звʼязуванні «імʼя поля → обʼєкт», а не на самому
обʼєкті. Згадай тему 03:
frozenзабороняє переклеїти ярлик, а не заглядати в коробку. - Рівень 3, вимірювання.
sys.getsizeof(обʼєкт)не рахує словник атрибутів, тож сам по собі бреше. Або додавайsys.getsizeof(обʼєкт.__dict__), або міряйtracemallocна великій кількості обʼєктів — так, як у практиці. - Перевіряти зручно на коротких іменах:
Ч("а", 1998, "к"). Механізм той самий, а друкувати менше.