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

Ітератори й генератори

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

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

Ти обійшов zip(товари, ціни) циклом for, а потім написав другий такий самий цикл — і він не надрукував жодного рядка й не впав. Чому?

Чому так. Список — ітерований обʼєкт: на кожне iter() він видає новий обхідник із позицією нуль, тому обходити його можна скільки завгодно. zip — уже ітератор: iter(пари) is пари дає True, тож другий for отримує той самий обхідник із вичерпаною позицією і завершується, не зробивши жодного кроку. Вихідні списки при цьому цілі — перевір їх після циклу.
Питання 2 / 8

Першим рядком у тілі генераторної функції стоїть print("почали"). Що надрукується в момент виклику г = квадрати(3)?

Чому так. Функція, у тілі якої є yield, при виклику лише запамʼятовує аргументи й повертає обʼєкт-генератор. Перша інструкція тіла виконається аж на першому next(г) — тоді й зʼявиться «почали». Це найчастіше джерело здивування: код усередині генератора виглядає як написаний, але не запускається, доки хтось не почне читати значення.
Питання 3 / 8

Генератор заснув на yield. З якого місця почне роботу наступний виклик next()?

Чому так. yield не завершує функцію, а заморожує її кадр: локальні змінні, точку зупинки — усе. Наступний next() розморожує кадр і йде далі з наступного рядка. Саме тому в прикладі з лекції змінна номер між викликами не обнулюється, а росте: 0, 1, 2. Побачити це можна власними очима через г.gi_frame.f_locals.
Питання 4 / 8

sys.getsizeof для списку з мільйона квадратів дає 8.06 МБ, а tracemalloc показує пік 38.6 МБ. Звідки взялася різниця майже в пʼять разів?

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

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

Чому так. Дані в конвеєрі не течуть уперед, їх тягнуть назад: останній генератор просить один елемент у середнього, той — один рядок у першого. Рядок, що не пройшов поріг, змушує попросити наступний, тому на три результати пішло 10 рядків — дорогих у журналі близько 30 %. Варіант «рівно 3» був би правильний, якби кожен рядок проходив фільтр; варіант «мільйон» описує роботу через списки.
Питання 6 / 8

Чому len(генератор) кидає TypeError, хоча кількість елементів у ньому цілком визначена?

Чому так. Довжина генератора не «визначена наперед»: щоб її дізнатись, треба виконати все тіло до StopIteration, а тим самим і спожити всі значення. Функція, яка мовчки руйнує свій аргумент, — погана функція, тому __len__ генератору свідомо не дали. Якщо кількість потрібна, збери list(генератор) і питай довжину в нього. Заразом: __iter__ у генератора якраз є — він віддає самого себе.
Питання 7 / 8

Потрібне джерело, яке лишається лінивим, але яке можна обходити багато разів. Що зробити?

Чому так. Ключ — у різниці між ітерованим і ітератором. Якщо __iter__ повертає self, клас сам є ітератором і одноразовий; якщо повертає новий генератор, то поводиться як список: кожен for починає спочатку, а памʼять не витрачається. iter() від генератора віддає його ж самого, тож нічого не рятує; reset() неможливий, бо після вичерпання кадр функції вже звільнено. Для двох проходів одразу є ще itertools.tee.
Питання 8 / 8

У журналі назва «кава» трапляється тричі, але groupby по товару дала три окремі групи «кава» по одному елементу. Що не так?

Чому так. groupby нічого не збирає в памʼять і йде джерелом один раз: щойно ключ змінився, поточна група закривається. Тому вона розрізає перемішані дані на десятки дрібних груп. Ліки — sorted(дані, key=…) тим самим ключем перед групуванням. І окремо памʼятай: група теж ітератор, і якщо перейти до наступної, не прочитавши поточну, вона зникне назавжди.