Итераторы и генераторы: вопросы для собеседования (часть 2)

yield, генераторные выражения, протокол итератора (__iter__, __next__) — продвинутый Python, который спрашивают у кандидатов на middle+. Генераторы экономят память при обработке больших данных. На собеседовании просят объяснить ленивое вычисление, написать генератор или показать разницу между списком и генератором.

Коллекции и структуры данныхГенераторы списков и встроенные функцииЦиклы и условияИсключения и отладкаРабота с файлами: JSON и CSVФункции и аргументыNumPy: основыPandas и DataFrameСинтаксис и типы данных

Вопросы 610 из 20

6Дан генератор: `def gen(): print('A'); yield 1; print('B'); yield 2`. Сразу после `g = gen()` что будет выведено в консоль?
AВыведется `A`.
BВыведутся `A` и `B`.
CНичего не выведется, пока не начать итерацию (например, `next(g)`).
DБудет выброшено исключение `TypeError`.
Ответ: Код внутри генератора выполняется только при итерации, до первого `yield`.

Вызов `g = gen()` создаёт объект генератора, но не запускает тело функции. Тело начнёт выполняться при `next(g)` или при цикле `for x in g`. Поэтому до начала итерации ничего не печатается.

7Функция-генератор `def evens(xs): for x in xs: if x % 2 == 0: yield x`. Чему равен результат `list(evens([1, 2, 3, 4]))`?
A`[1, 2, 3, 4]`
B`[1, 3]`
C`[2, 4]`
D2
Ответ: `yield` отдаёт только подходящие значения, а `list(...)` материализует генератор.

Генератор проходит по `xs` и делает `yield x` только для чётных `x`. Поэтому при входе `[1, 2, 3, 4]` будут отданы 2 и 4. Оборачивание в `list(...)` просто собирает все выданные элементы в список.

8Что обычно верно про `(row for row in rows)` по сравнению с `[row for row in rows]`, если `rows` — большой поток данных?
AОба создают список в памяти одинакового размера.
BГенераторное выражение всегда быстрее списка.
CСписок нельзя итерировать, а генератор можно.
DГенераторное выражение вычисляет элементы лениво и обычно экономит память, но его результат одноразовый.
Ответ: Генератор — ленивый и потоковый, список — материализованный и подходит для повторных проходов.

List comprehension `[row for row in rows]` создаёт весь список сразу и хранит его в памяти. Generator expression `(row for row in rows)` возвращает итератор, который выдаёт элементы по мере запроса, поэтому чаще экономит память на больших данных. Но генератор нельзя «перемотать»: после одного прохода он исчерпывается.

9Есть `it = iter([5])`. Выполнили `next(it)`, а затем `next(it, -1)`. Что вернёт второй вызов `next(it, -1)`?
A5
B-1
C`None`
DИсключение `StopIteration`.
Ответ: `next(it, default)` возвращает `default` вместо `StopIteration`, если iterator исчерпан.

После первого `next(it)` единственный элемент 5 уже прочитан. Второй вызов `next(it, -1)` не может получить новое значение, но вместо исключения вернёт значение по умолчанию — -1. Это удобно, когда хотите безопасно попытаться взять элемент без обработки `StopIteration`.

10Создали `g = (x for x in [1, 2, 3])`, затем `s1 = sum(g)` и `s2 = sum(g)`. Чему равно `s2`?
A6
B3
C0
DБудет ошибка `TypeError`.
Ответ: Generator expression исчерпывается после первого прохода; второй раз `sum` увидит пустой поток.

При первом `sum(g)` генератор отдаёт 1, 2, 3 и исчерпывается. Второй `sum(g)` суммирует уже пустой iterator, а сумма пустой последовательности в Python — 0. Это типичный баг в аналитике: один и тот же генератор используют для нескольких метрик.

1234

Хотите тренировать интерактивно?

В приложении — таймер, прогресс, стрики и 1700+ вопросов по всем темам.

Тренировать в Telegram

Другие темы: Python

Коллекции и структуры данныхГенераторы списков и встроенные функцииЦиклы и условияИсключения и отладкаРабота с файлами: JSON и CSVФункции и аргументыNumPy: основыPandas и DataFrameСинтаксис и типы данных