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

Магічні методи

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

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

У класі Каталог визначено лише __len__. Що поверне bool(Каталог([])) — тобто порожній каталог у перевірці if?

Чому так. Істинність перевіряється трьома сходинками: спершу __bool__, потім __len__, потім «істинний за замовчуванням». __bool__ немає, тому спрацьовує друга сходинка: довжина нуль → обʼєкт хибний. Перший варіант правильний лише тоді, коли немає ані __bool__, ані __len__ — і саме це та тиха пастка, через яку порожній контейнер потрапляє в гілку «є що показати».
Питання 2 / 8

Клас має __eq__, який порівнює за вмістом, і рядок __hash__ = object.__hash__, дописаний, щоб прибрати TypeError. Що станеться з двома рівними обʼєктами в множині?

Чому так. Хеш від object рахується з адреси, тож рівні обʼєкти дають різні хеші, лягають у різні комірки й ніколи не зустрічаються — перевіряти рівність просто ніде. Перший варіант — найпоширеніша хибна очікуваність: рівність перевіряється лише всередині однієї комірки, а потрапити в одну комірку можна тільки з однаковим хешем. Так виглядає порушений контракт у третьому інтерактиві лекції: len({к1, к2}) = 2.
Питання 3 / 8

У класі є тільки __lt__ і жодного іншого методу порівняння. Який із цих чотирьох рядків впаде з TypeError?

Чому так. «Більше» Python виводить дзеркально: a > b — це b.__lt__(a). sorted, min і max усередині користуються лише операцією «менше», тому їм теж вистачає __lt__. А «не менше» з «менше» в загальному випадку не виводиться, тому <= падає — саме на ньому найчастіше спотикаються. Усі чотири одразу дає @total_ordering.
Питання 4 / 8

У класі визначено ЛИШЕ __str__. Що надрукує print([видання]) — тобто обʼєкт усередині списку?

Чому так. Контейнери друкують свої елементи через repr, а не str. __repr__ ми не визначили, тому спрацьовує версія з object — клас і адреса. Звʼязок працює лише в один бік: __str__ за замовчуванням віддає роботу __repr__, але не навпаки. Тому правило й звучить так: робиш один метод — роби __repr__.
Питання 5 / 8

Усередині блоку with кинуто виняток. Менеджер контексту має __exit__, який закриває файл і закінчується рядком return False. Що буде далі?

Чому так. __exit__ викликається завжди — і після нормального завершення, і після винятку; у другому випадку він отримує тип, значення й слід замість трьох None. Повернене значення вирішує долю винятку: False (і будь-яке хибне, включно з відсутнім return) означає «прибрав, але не лікував». Проковтнув би він виняток лише при return True — а це той самий голий except: pass, тільки гірше помітний.
Питання 6 / 8

У класі Каталог є тільки __getitem__, який передає індекс у внутрішній список. Чи спрацює цикл for в in каталог?

Чому так. Це старий протокол перебору, який Python досі підтримує: за відсутності __iter__ він звертається за номерами й зупиняється на IndexError — виняток тут сигнал «усе», а не поломка. Нескінченним цикл став би, якби __getitem__ повертав, скажімо, None замість того, щоб кинути виняток; наш метод передає індекс списку, а той кидає IndexError сам.
Питання 7 / 8

Виклик sum([Гроші(100), Гроші(200)]) падає з TypeError, хоча метод __add__ у класі Гроші є. Чому?

Чому так. sum починає з початкового значення 0, тому першим виконується int.__add__(0, Гроші(100)) — а int не знає, що робити з нашим типом. Виправлень два: дописати дзеркальний __radd__, який пропускає нуль, або просто передати початкове значення — sum(ціни, Гроші(0)). Повертати новий обʼєкт — навпаки, правильна поведінка: так само поводяться всі числа.
Питання 8 / 8

Що з переліченого — НАЙГІРША причина визначити магічний метод?

Чому так. Межа розумного проходить там, де читачеві доводиться гадати. Оператор має означати те саме, що для вбудованих типів; зсув бітів у ролі стрілочки не означає нічого, і рядок стає ребусом, який неможливо зрозуміти, не відкривши клас. Перші три випадки роблять обʼєкт схожим на вбудований тип — саме те, заради чого магічні методи й існують.