Ти обійшов zip(товари, ціни) циклом for, а потім написав другий такий самий цикл — і він не надрукував жодного рядка й не впав. Чому?
iter() він видає новий обхідник із позицією нуль, тому обходити його можна скільки завгодно. zip — уже ітератор: iter(пари) is пари дає True, тож другий for отримує той самий обхідник із вичерпаною позицією і завершується, не зробивши жодного кроку. Вихідні списки при цьому цілі — перевір їх після циклу.Першим рядком у тілі генераторної функції стоїть print("почали"). Що надрукується в момент виклику г = квадрати(3)?
yield, при виклику лише запамʼятовує аргументи й повертає обʼєкт-генератор. Перша інструкція тіла виконається аж на першому next(г) — тоді й зʼявиться «почали». Це найчастіше джерело здивування: код усередині генератора виглядає як написаний, але не запускається, доки хтось не почне читати значення.Генератор заснув на yield. З якого місця почне роботу наступний виклик next()?
yield не завершує функцію, а заморожує її кадр: локальні змінні, точку зупинки — усе. Наступний next() розморожує кадр і йде далі з наступного рядка. Саме тому в прикладі з лекції змінна номер між викликами не обнулюється, а росте: 0, 1, 2. Побачити це можна власними очима через г.gi_frame.f_locals.sys.getsizeof для списку з мільйона квадратів дає 8.06 МБ, а tracemalloc показує пік 38.6 МБ. Звідки взялася різниця майже в пʼять разів?
sys.getsizeof повертає розмір одного обʼєкта й не заглядає всередину: для списку це заголовок плюс масив із мільйона посилань по 8 байтів. Самі числа — окремі обʼєкти в купі, і їх видно лише наскрізному обліку tracemalloc. Практичний висновок: для контейнерів getsizeof завжди применшує, і покладатись на нього як на «скільки це коштує» не можна.Лінивий конвеєр із трьох генераторів над журналом на мільйон рядків віддав три дорогі продажі. Скільки рядків журналу при цьому прочитала перша ланка?
Чому len(генератор) кидає TypeError, хоча кількість елементів у ньому цілком визначена?
StopIteration, а тим самим і спожити всі значення. Функція, яка мовчки руйнує свій аргумент, — погана функція, тому __len__ генератору свідомо не дали. Якщо кількість потрібна, збери list(генератор) і питай довжину в нього. Заразом: __iter__ у генератора якраз є — він віддає самого себе.Потрібне джерело, яке лишається лінивим, але яке можна обходити багато разів. Що зробити?
__iter__ повертає self, клас сам є ітератором і одноразовий; якщо повертає новий генератор, то поводиться як список: кожен for починає спочатку, а памʼять не витрачається. iter() від генератора віддає його ж самого, тож нічого не рятує; reset() неможливий, бо після вичерпання кадр функції вже звільнено. Для двох проходів одразу є ще itertools.tee.У журналі назва «кава» трапляється тричі, але groupby по товару дала три окремі групи «кава» по одному елементу. Що не так?
groupby нічого не збирає в памʼять і йде джерелом один раз: щойно ключ змінився, поточна група закривається. Тому вона розрізає перемішані дані на десятки дрібних груп. Ліки — sorted(дані, key=…) тим самим ключем перед групуванням. І окремо памʼятай: група теж ітератор, і якщо перейти до наступної, не прочитавши поточну, вона зникне назавжди.