yield, генераторные выражения, протокол итератора (__iter__, __next__) — продвинутый Python, который спрашивают у кандидатов на middle+. Генераторы экономят память при обработке больших данных. На собеседовании просят объяснить ленивое вычисление, написать генератор или показать разницу между списком и генератором.
После `g = gen()` Python создаёт объект-генератор, но код внутри функции ещё не запущен — `print("A")` не сработает. Тело начнёт исполняться только при первой итерации: `next(g)` или `for x in g` дойдёт до первого `yield 1`, и тогда увидим `A`. Следующий `next(g)` напечатает `B` и вернёт `2`. `TypeError` не возникает: вызов `gen()` без аргументов корректен, ведь функция не принимает параметров.
Подробный разбор →`evens(xs)` для каждого элемента проверяет `x % 2 == 0` и делает `yield x` только для чётных. `list(evens([1, 2, 3, 4]))` собирает все выданные значения в список — `[2, 4]`. Нечётные просто пропускаются: `yield` для них не выполняется. Это не функция, считающая количество чётных, поэтому `2` тоже не подходит.
Подробный разбор →Списочное включение `[row for row in rows]` создаёт весь список сразу и хранит его в памяти. Генераторное выражение `(row for row in rows)` возвращает итератор, который выдаёт элементы по мере запроса, поэтому чаще экономит память на больших данных. Но генератор нельзя «перемотать»: после одного прохода он исчерпывается. Списки одинакового размера — миф; «всегда быстрее» — слишком сильное утверждение; повторно итерировать можно как раз список, а не генератор.
Подробный разбор →Первый `next(it)` забрал единственный элемент `5` и продвинул итератор за конец. Когда у `next` указан второй аргумент-«дефолт», на исчерпанном итераторе вместо исключения возвращается это значение. Поэтому второй `next(it, -1)` отдаёт `-1`. Без второго аргумента вызов поднял бы `StopIteration`, но в задаче он явно передан. `None` появляется только если его передать вторым аргументом самому, а первый элемент списка повторно не возвращается.
Подробный разбор →`g = (x for x in [1, 2, 3])` создаёт одноразовый генератор. Первый `sum(g)` обходит его до конца и возвращает `6`. После этого внутри `g` элементов уже нет, поэтому второй `sum(g)` — это сумма пустой последовательности, а она по определению равна `0`. Никакого автоматического «перезапуска» не происходит, и исключение `sum` не выбрасывает. Чтобы получить `6` ещё раз, нужно создать генератор заново.
Подробный разбор →В приложении — таймер, прогресс, стрики и 1700+ вопросов по всем темам.
Тренировать Python в Telegram