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

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

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

Вопросы 1115 из 20

11Функция-генератор `squares(n)` делает `yield i * i` для `i` из `range(n)`. Чему равно `list(squares(3))`?
A`[1, 4, 9]`
B`[0, 1, 4]`
C`[0, 1, 4, 9]`
DБудет ошибка `NameError`.
Ответ: Генератор с `yield` отдаёт квадраты для `i` из `range(n)`, начиная с 0.

`range(n)` генерирует числа от 0 до `n - 1`. Для `n = 3` это 0, 1, 2, а квадраты — 0, 1, 4. `list(...)` собирает все значения, которые отдаёт `yield`.

12Вы фильтруете большой поток строк `rows`. Почему генератор `def valid(rows): for row in rows: if is_valid(row): yield row` часто лучше по памяти, чем `[row for row in rows if is_valid(row)]`?
AПотому что `yield` автоматически кэширует все строки на диске.
BПотому что list comprehension нельзя использовать в `for`.
CПотому что генератор отдаёт элементы по одному и не хранит весь результат сразу в памяти.
DПотому что генератор гарантированно работает быстрее списка в любом случае.
Ответ: Генератор через `yield` — потоковый: позволяет обрабатывать данные постепенно без полной материализации.

List comprehension создаёт список всех валидных строк сразу, что может быть дорого по памяти. Генератор с `yield` выдаёт элементы по одному, поэтому вы можете сразу передавать их в следующий шаг пайплайна (`for row in valid(rows): ...`) без хранения всего набора. Это типичный приём для экономии памяти на больших логах.

13Какой объект является iterator и может быть напрямую использован в `next(obj)`?
A`iter([1, 2])`
B`[1, 2]`
C`'ab'`
D`{1, 2}`
Ответ: `next()` работает с итератором; `iterable` (список, строка, set) сначала нужно обернуть в `iter()`.

Список, строка и set — iterable: по ним можно итерироваться в `for`, но они не являются iterator (у них нет текущего состояния «где мы»). Вызов `iter([1, 2])` создаёт iterator, у которого можно вызывать `next(...)`.

14Есть `it = iter([1, 2, 3])`. Выполнили `next(it)` два раза, а затем запустили цикл `for x in it: print(x)`. Что будет напечатано?
AБудет напечатано 1, 2, 3.
BБудет напечатано только 3.
CБудет напечатано только 1.
DНичего не будет напечатано.
Ответ: Iterator продолжает с текущей позиции; `next()` уже «съел» первые элементы.

Два вызова `next(it)` последовательно забрали 1 и 2. Цикл `for` продолжит чтение того же iterator и получит только оставшееся значение 3. Это удобно, но может стать багом, если вы не ожидали, что iterator разделяется между `next()` и `for`.

15Дан генератор `def gen(): yield 1; yield 2; yield 3`. Выполнили `g = gen()`, `first = next(g)`, `rest = list(g)`. Чему равно `rest`?
A`[1, 2, 3]`
B`[1, 2]`
C`[3]`
D`[2, 3]`
Ответ: Генератор хранит состояние: после `next(g)` он продолжит с места остановки.

Вызов `next(g)` забирает первый `yield` (1) и переносит исполнение генератора дальше. Затем `list(g)` забирает оставшиеся значения, которые ещё не были выданы: 2 и 3. Поэтому `rest` равно `[2, 3]`.

1234

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

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

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

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

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