В чем разница между итератором, генератором и корутиной в Python

Ответ

Это связанные, но разные концепции в Python, которые служат для последовательной обработки данных и асинхронного программирования.

1. Итератор (Iterator)

Это объект, представляющий поток данных. Он реализует протокол итератора, который состоит из двух методов:

  • __iter__(): Возвращает сам объект-итератор.
  • __next__(): Возвращает следующий элемент из последовательности. Когда элементы заканчиваются, вызывает исключение StopIteration.

Любой объект, который можно перебрать в цикле for, является итерируемым (iterable), а сам цикл неявно создает из него итератор.

Пример (класс-итератор):

class Counter:
    def __init__(self, limit):
        self.limit = limit
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < self.limit:
            self.current += 1
            return self.current
        else:
            raise StopIteration

# Использование
for num in Counter(3):
    print(num) # 1, 2, 3

2. Генератор (Generator)

Это простой способ создания итераторов. Генератор — это функция, которая использует ключевое слово yield для возврата значений. При каждом вызове yield выполнение функции приостанавливается, а ее состояние сохраняется. Это позволяет создавать "ленивые" последовательности, которые генерируют значения на лету и не хранят их все в памяти.

Пример (функция-генератор):

def counter_generator(limit):
    current = 0
    while current < limit:
        current += 1
        yield current

# Использование
for num in counter_generator(3):
    print(num) # 1, 2, 3

Ключевая идея: Все генераторы являются итераторами, но не наоборот.

3. Корутина (Coroutine)

Корутина (или сопрограмма) — это обобщение концепции генератора, предназначенное для кооперативной многозадачности. В современном Python корутины объявляются с помощью async def.

Они могут приостанавливать свое выполнение в точках await, отдавая управление циклу событий (event loop), который может запустить другую задачу. Это основа асинхронного программирования в asyncio.

Пример (корутина):

import asyncio

async def my_coroutine():
    print("Начало выполнения корутины")
    # Приостанавливаем выполнение на 1 секунду, 
    # отдавая управление другим задачам
    await asyncio.sleep(1)
    print("Корутина возобновила работу и завершается")

asyncio.run(my_coroutine())

Итог:

  • Итератор: Базовый объект для пошагового перебора.
  • Генератор: Удобная "фабрика" итераторов с помощью yield.
  • Корутина: "Прокачанный" генератор (async/await) для асинхронных операций.

Ответ 18+ 🔞

Слушай, а вот эти ваши итераторы-генераторы-корутины в Python — это ж, блядь, как слоёный пирог из трёх разных тестов, которые друг на друге сидят, но каждый со своим характером. Ёпта, сейчас разложу по полочкам, чтобы даже кот, который на клавиатуре спит, понял.

Итератор — это, сука, самый простой и тупой солдафон в этой истории. Его задача — тупо шагать по данным и тыкать пальцем в следующий элемент. У него два заклинания: __iter__ (который говорит «я сам себе итератор, нахуй») и __next__ (который выдаёт следующий кусок, а когда всё кончилось — орёт StopIteration). Любой цикл for внутри себя именно это и делает — создаёт из твоей коллекции этого тупого робота-указку.

Вот, смотри, как он выглядит, этот деревянный Буратино:

class Counter:
    def __init__(self, limit):
        self.limit = limit
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < self.limit:
            self.current += 1
            return self.current
        else:
            raise StopIteration

# Использование
for num in Counter(3):
    print(num) # 1, 2, 3

Видишь? Написали класс, как будто из учебника по столярному делу. Работает? Работает. Изящно? Нихуя. Поэтому придумали...

Генератор — это, блядь, ленивая жопа, которая не хочет всё считать и хранить в памяти сразу. Она говорит: «На, держи одно значение, а остальные я тебе потом, когда попросишь, выдам». Внутри обычная функция, но вместо return стоит волшебное слово yield. Каждый раз, когда ты у неё что-то просишь, она выдаёт значение и засыпает до следующего пинка. Все генераторы — это итераторы, но с изюминкой, ёпта!

Смотри, как та же хуйня, но в десять раз короче и элегантнее:

def counter_generator(limit):
    current = 0
    while current < limit:
        current += 1
        yield current

# Использование
for num in counter_generator(3):
    print(num) # 1, 2, 3

Красота, да? Никаких классов, никаких raise StopIteration — просто yield и пошёл-поехал. Но это ещё не предел, потому что дальше идёт...

Корутина — это, сука, генератор, который съел стероидов для асинхронного программирования и стал супергероем. Если генератор умеет только отдавать значения, то корутина умеет ещё и принимать их извне, а главное — ждать, не блокируя весь мир вокруг. Объявляется через async def, а ключевое слово await — это её точка остановки, где она говорит: «Я тут подожду, а вы, другие корутины, работайте, не стесняйтесь».

Вот, полюбуйся на эту мартышку:

import asyncio

async def my_coroutine():
    print("Начало выполнения корутины")
    # Приостанавливаем выполнение на 1 секунду, 
    # отдавая управление другим задачам
    await asyncio.sleep(1)
    print("Корутина возобновила работу и завершается")

asyncio.run(my_coroutine())

Видишь await? Это она прилегла на секундочку, а в это время цикл событий мог другую задачку погонять. Это основа всей асинхронщины, без которой сейчас ни один уважающий себя сервис не живёт.

Итог, блядь, короткий и ясный:

  • Итератор — тупой робот-указка, шагает по данным.
  • Генератор — ленивая жопа с yield, выдаёт значения по одному.
  • Корутина — прокачанный генератор с async/await, умеет ждать и не блокировать поток.

Всё, теперь можешь идти и блеснуть этим знанием на собеседовании, только не перепутай, а то опозоришься, как тот Герасим, который только «Муму» и мог сказать.