Итераторы и генераторы: вопросы для собеседования (часть 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Ничего не будет выведено
DБудет исключение `TypeError`
Ответ: Вызов функции-генератора возвращает объект-генератор, но тело функции не выполняется до первой итерации.

После `g = gen()` Python создаёт объект-генератор, но код внутри функции ещё не запущен — `print("A")` не сработает. Тело начнёт исполняться только при первой итерации: `next(g)` или `for x in g` дойдёт до первого `yield 1`, и тогда увидим `A`. Следующий `next(g)` напечатает `B` и вернёт `2`. `TypeError` не возникает: вызов `gen()` без аргументов корректен, ведь функция не принимает параметров.

Подробный разбор →
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Список `[2, 4]`
CСписок `[1, 3]`
DЦелое число `2`
Ответ: Функция-генератор отдаёт значения только при `yield`, поэтому в результат попадают лишь элементы, прошедшие условие фильтра.

`evens(xs)` для каждого элемента проверяет `x % 2 == 0` и делает `yield x` только для чётных. `list(evens([1, 2, 3, 4]))` собирает все выданные значения в список — `[2, 4]`. Нечётные просто пропускаются: `yield` для них не выполняется. Это не функция, считающая количество чётных, поэтому `2` тоже не подходит.

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

Списочное включение `[row for row in rows]` создаёт весь список сразу и хранит его в памяти. Генераторное выражение `(row for row in rows)` возвращает итератор, который выдаёт элементы по мере запроса, поэтому чаще экономит память на больших данных. Но генератор нельзя «перемотать»: после одного прохода он исчерпывается. Списки одинакового размера — миф; «всегда быстрее» — слишком сильное утверждение; повторно итерировать можно как раз список, а не генератор.

Подробный разбор →
9Есть `it = iter([5])`. Выполнили `next(it)`, а затем `next(it, -1)`. Что вернёт второй вызов `next(it, -1)`?
AЦелое число `5` повторно
BЗначение по умолчанию `-1`
CПустое значение `None`
DИсключение `StopIteration`
Ответ: Если у `next()` указан второй аргумент, он возвращается вместо `StopIteration` при исчерпанном итераторе.

Первый `next(it)` забрал единственный элемент `5` и продвинул итератор за конец. Когда у `next` указан второй аргумент-«дефолт», на исчерпанном итераторе вместо исключения возвращается это значение. Поэтому второй `next(it, -1)` отдаёт `-1`. Без второго аргумента вызов поднял бы `StopIteration`, но в задаче он явно передан. `None` появляется только если его передать вторым аргументом самому, а первый элемент списка повторно не возвращается.

Подробный разбор →
10Создали `g = (x for x in [1, 2, 3])`, затем `s1 = sum(g)` и `s2 = sum(g)`. Чему равно `s2`?
AЦелое число `6`
BЦелое число `3`
CЦелое число `0`
DИсключение `TypeError`
Ответ: Генератор обходится один раз: после первого полного прохода последовательность считается пустой.

`g = (x for x in [1, 2, 3])` создаёт одноразовый генератор. Первый `sum(g)` обходит его до конца и возвращает `6`. После этого внутри `g` элементов уже нет, поэтому второй `sum(g)` — это сумма пустой последовательности, а она по определению равна `0`. Никакого автоматического «перезапуска» не происходит, и исключение `sum` не выбрасывает. Чтобы получить `6` ещё раз, нужно создать генератор заново.

Подробный разбор →
1234

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

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

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

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

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