Что такое итераторы и генераторы в Python и в чем их разница
Что проверяют
Понимаете ли вы протокол итерации и то, что генератор это способ его реализовать, не описывая класс. Отсюда обычно переходят к ленивым вычислениям и памяти.
Как отвечать
Итератор это объект с методами __iter__ и __next__, который отдаёт элементы по одному и бросает StopIteration, когда они кончились. Всё, что перебирается циклом for, работает через этот протокол.
Итерируемый объект это то, у чего есть __iter__: список, строка, словарь. Список сам не итератор, но умеет его создать.
Генератор это функция с yield. Вызов такой функции не выполняет тело, а возвращает генератор, готовый итератор с уже реализованным протоколом.
def countdown(n):
while n > 0:
yield n
n -= 1Главное практическое отличие это память. Список из миллиона элементов лежит в памяти целиком, генератор считает по одному:
sum(x * x for x in range(10_000_000)) # генераторное выражение, память постояннаЕщё генератор хранит состояние между вызовами (место, где остановился) и одноразовый: пройти по нему второй раз нельзя, нужно создавать заново.
Где спотыкаются
Путают итерируемое и итератор. Проверка простая: у итератора iter(obj) is obj.
Считают генератор быстрее списка. Он экономит память, а не такты; по скорости на маленьких объёмах список часто выигрывает.
Пробуют взять len() от генератора или пройти дважды и удивляются пустому результату.
Не знают, что yield умеет и принимать значения (.send()), и что на этом построены сопрограммы.