Домашнє завдання

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

Предметна область тут інша, ніж у лекції, — навмисно: якщо ти зможеш повторити ту саму механіку на своїх даних, значить, справа не в завченому прикладі. Працюємо з показаннями датчика температури: довгий потік чисел, який не влазить у памʼять і який цікавий не весь, а по шматочках. Усе роби в окремому файлі datchyk.py або в новому зошиті — практику не редагуй, вона знадобиться для звірки.


рівень 1Рівень 1 — База

Напиши те саме джерело двічі — класом і генератором — і доведи, що вони нерозрізненні.

  1. Клас Показання з методами __iter__ і __next__: віддає скільки значень температури, обчислюючи кожне за формулою 20 + (номер * 37) % 15 (жодної випадковості — числа мають повторюватись у всіх).
  2. Генераторна функція показання(скільки), яка робить рівно те саме через yield.
  3. Порахуй і надрукуй, скільки рядків коду вийшло в кожному варіанті.
  4. Заміряй sys.getsizeof для показання(1_000_000) і для list(показання(1_000_000)) і надрукуй обидва числа з підписами.

Зроблено, якщо:


рівень 2Рівень 2 — Плюс

Збудуй лінивий конвеєр із трьох ланок і доведи лічильниками, що зайвої роботи немає.

  1. Три генератори: показання(скільки)у_фаренгейтах(значення)лише_спека(значення, поріг=90). Кожна ланка бере попередню як аргумент.
  2. У кожну ланку встав лічильник викликів (наприклад, у спільний словник лічильник = {"джерело": 0, "переведено": 0, "віддано": 0}).
  3. Попроси у конвеєра перші пʼять значень і зупинись. Надрукуй лічильники.
  4. Поруч зроби чесний варіант через списки: спершу весь список показань, потім весь список у Фаренгейтах, потім фільтр. Надрукуй його лічильники теж.
  5. Порівняй tracemalloc-ом пік памʼяті обох варіантів на мільйоні показань.

Зроблено, якщо:


рівень 3Рівень 3 — Виклик

Досліди межу лінощів: де вона економить, а де починає коштувати дорожче.

  1. Зроби клас Датчик, чий __iter__ щоразу створює новий генератор. Доведи трьома assert-ами: (а) два for поспіль дають однакові списки, (б) обхід не будує список у памʼяті (sys.getsizeof(iter(датчик)) < 1000), (в) а клас Показання з рівня 1 на другому обході дає порожньо.
  2. Спіймай і запиши дослівно тексти трьох помилок: len(генератор), генератор[0], reversed(генератор). Для кожної напиши одним реченням, чому інакше й бути не могло.
  3. Візьми itertools.tee(показання(1_000_000), 2), вичерпай одну копію повністю, а другу не чіпай — і заміряй tracemalloc-ом, скільки памʼяті це коштувало. Поясни числом, чому tee не безкоштовний.
  4. Знайди й покажи кодом випадок, коли список швидший за генератор: заміряй time.perf_counter для sum по списку й по генератору на 10, 1 000 і 1 000 000 елементів. Побудуй табличку з трьох рядків.
  5. Напиши генератор ковзне_середнє(значення, вікно), який віддає середнє останніх вікно показань, не тримаючи в памʼяті більше за вікно чисел. Доведи assert-ом на коротких даних, що результат збігається з наївним розрахунком через зрізи списку.

Зроблено, якщо:


Підказки