Вопрос с собеседования · Python

Чем итератор отличается от итерируемого объекта?

JuniorИтераторы и генераторы
Ещё спрашивают так:
  • Что такое протокол итератора?
  • Как работает цикл for под капотом?
  • Что такое __iter__ и __next__?
Коротко

Итерируемый объект умеет выдать итератор (__iter__), итератор выдаёт значения по одному (__next__).

  • for вызывает iter(obj), затем next() до StopIteration
  • Список итерируемый, но не итератор: его можно обходить много раз

А ещё Итератор одноразовый, его __iter__ возвращает сам себя

Подробно

Итерируемый объект (iterable) — всё, что можно обойти в for: список, строка, словарь, файл, range. У него есть метод __iter__, который возвращает новый итератор. (Старый вариант — __getitem__ с индексами от 0 — тоже поддерживается.)

Итератор — объект с текущей позицией. Его __next__ возвращает следующий элемент или бросает StopIteration, когда элементы кончились. Его __iter__ возвращает self, поэтому итератор тоже можно передать в for.

Цикл for x in obj разворачивается так:

  1. it = iter(obj);
  2. в цикле x = next(it);
  3. при StopIteration — выход.

Следствия: список можно обходить сколько угодно раз — каждый for получает новый итератор. Итератор (и генератор) исчерпывается за один проход.

Свой итерируемый класс проще всего сделать, написав __iter__ как генератор.

Пример

nums = [1, 2]
it = iter(nums)
next(it), next(it)     # (1, 2)
next(it)               # StopIteration

class Countdown:
    def __init__(self, start):
        self.start = start
    def __iter__(self):          # каждый for — новый генератор
        n = self.start
        while n > 0:
            yield n
            n -= 1

list(Countdown(3))     # [3, 2, 1]
list(Countdown(3))     # [3, 2, 1] — можно снова

Что хотят услышать

  • Чёткое разделение двух понятий и их методов.
  • Как работает for через iter и next.
  • Одноразовость итератора и многоразовость коллекции.

Частые ошибки

  • Называть список итератором.
  • Делать класс, который является сам себе итератором, и удивляться, что второй проход пустой.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✓Итерируемый объект выдаёт итератор (__iter__), итератор отдаёт значения через __next__ и одноразовый
  • ✗Список сам является итератором с __next__, поэтому после одного обхода в for его нельзя обойти повторно
  • ✗for вызывает __next__ у итерируемого объекта напрямую, без iter()
  • ✗Итератор можно перезапустить с начала вызовом iter()

Что спросят дальше

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования