← Назад к вопросам

Как понимаешь фразу, что любой генератор это итератор, но не любой итератор это генератор?

2.3 Middle🔥 141 комментариев
#Python Core#Soft Skills

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Генератор vs Итератор: "Любой генератор это итератор, но не любой итератор это генератор"

Это классическая фраза объясняет иерархию: все генераторы — это итераторы, но есть итераторы, которые не генераторы. Разберу подробно с кодом.

1. Определение: Итератор

Итератор — это объект который:

  • Имеет метод __next__() — возвращает следующее значение
  • Имеет метод __iter__() — возвращает сам себя
  • Вызывает StopIteration когда значения закончились
# Пример простого итератора
class CountUpTo:
    def __init__(self, max):
        self.current = 0
        self.max = max
    
    def __iter__(self):
        """Возвращает сам себя"""
        return self
    
    def __next__(self):
        """Возвращает следующее значение"""
        if self.current < self.max:
            self.current += 1
            return self.current
        else:
            raise StopIteration

# Использование
iterator = CountUpTo(3)
print(next(iterator))  # 1
print(next(iterator))  # 2
print(next(iterator))  # 3
# print(next(iterator))  # StopIteration

# Можем использовать в for
for value in CountUpTo(3):
    print(value)  # 1, 2, 3

2. Определение: Генератор

Генератор — это частный случай итератора. Создаётся с помощью функции с ключевым словом yield:

# Генератор — это функция с yield
def count_up_to(max):
    current = 0
    while current < max:
        current += 1
        yield current

# Это генератор
gen = count_up_to(3)
print(type(gen))  # <class 'generator'>
print(isinstance(gen, Iterator))  # True — генератор это итератор!

# Использование
print(next(gen))  # 1
print(next(gen))  # 2
print(next(gen))  # 3

# Или в for
for value in count_up_to(3):
    print(value)  # 1, 2, 3

3. Главное отличие

Генератор — это:

  • Итератор с особенной природой
  • Создаётся функцией с yield
  • Автоматически имеет __next__() и __iter__()
  • Ленивое вычисление — значения генерируются при запросе
  • Состояние сохраняется между вызовами next()
def generator_example():
    print('Старт')
    yield 1
    print('После первого yield')
    yield 2
    print('После второго yield')
    yield 3
    print('Конец')

gen = generator_example()
print(next(gen))  # Выводит: 'Старт', потом 1
print(next(gen))  # Выводит: 'После первого yield', потом 2
print(next(gen))  # Выводит: 'После второго yield', потом 3

# next(gen)  # Выводит: 'Конец', потом StopIteration

4. Типизация и проверка

from collections.abc import Iterator, Generator
from types import GeneratorType

# Наш класс-итератор
class MyIterator:
    def __init__(self):
        self.value = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        self.value += 1
        if self.value > 3:
            raise StopIteration
        return self.value

# Функция-генератор
def my_generator():
    for i in range(1, 4):
        yield i

# Проверка типов
it = MyIterator()
gen = my_generator()

print(f'MyIterator:')
print(f'  isinstance(Iterator): {isinstance(it, Iterator)}')      # True
print(f'  isinstance(Generator): {isinstance(it, Generator)}')    # False
print(f'  hasattr(__next__): {hasattr(it, "__next__")}')          # True
print(f'  hasattr(send): {hasattr(it, "send")}')                  # False

print(f'\nmy_generator:')
print(f'  isinstance(Iterator): {isinstance(gen, Iterator)}')      # True
print(f'  isinstance(Generator): {isinstance(gen, Generator)}')    # True
print(f'  hasattr(__next__): {hasattr(gen, "__next__")}')          # True
print(f'  hasattr(send): {hasattr(gen, "send")}')                  # True !
print(f'  type(gen): {type(gen).__name__}')                       # generator

5. Дополнительные методы генератора

Генераторы имеют методы которых нет у обычных итераторов:

def my_gen():
    x = yield 1
    print(f'Получили: {x}')
    y = yield 2
    print(f'Получили: {y}')
    yield 3

gen = my_gen()

# next() = send(None)
print(next(gen))  # 1

# send() — отправляем значение в генератор
print(gen.send('Hello'))  # Выводит 'Получили: Hello', потом 2
print(gen.send('World'))  # Выводит 'Получили: World', потом 3

# try:
#     gen.send('Extra')
# except StopIteration:
#     print('Generator finished')

# Также есть методы:
gen2 = my_gen()
print(f'throw метод: {hasattr(gen2, "throw")}')  # True
print(f'close метод: {hasattr(gen2, "close")}')  # True
print(f'gi_frame: {hasattr(gen2, "gi_frame")}')   # True (доступ к фрейму)

6. Ленивость — главное преимущество генератора

# Итератор часто хранит все значения
class AllNumbers:
    def __init__(self, n):
        self.numbers = list(range(n))  # Все значения в памяти!
        self.index = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.index < len(self.numbers):
            result = self.numbers[self.index]
            self.index += 1
            return result
        raise StopIteration

# Генератор вычисляет значения по запросу
def all_numbers(n):
    for i in range(n):  # Не создаёт список!
        yield i

# Тест памяти
it = AllNumbers(1000000)  # Создаёт список из 1М элементов
gen = all_numbers(1000000)  # Не создаёт ничего, только функция

print(f'list(range(5)): {sys.getsizeof(list(range(5)))} bytes')
print(f'range(5): {sys.getsizeof(range(5))} bytes')

# Генератор занимает минимум памяти
import sys
print(f'Generator размер: {sys.getsizeof(gen)} bytes')

7. Практические примеры

Генератор — обработка больших файлов

# Чтение файла без загрузки в памяти
def read_large_file(file_path, chunk_size=1024):
    with open(file_path, 'rb') as f:
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break
            yield chunk

# Использование
for chunk in read_large_file('large_file.bin'):
    process_chunk(chunk)  # Обрабатываем по частям

Итератор — с состоянием

class Fibonacci:
    def __init__(self, max_count):
        self.a, self.b = 0, 1
        self.count = 0
        self.max_count = max_count
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.count < self.max_count:
            result = self.a
            self.a, self.b = self.b, self.a + self.b
            self.count += 1
            return result
        raise StopIteration

# Использование
for num in Fibonacci(7):
    print(num)  # 0 1 1 2 3 5 8

8. Встроенные примеры

# Встроенные генераторы
gen1 = (x*2 for x in range(5))  # Generator expression
print(type(gen1))  # <class 'generator'>

# Встроенные итераторы (но не генераторы)
it1 = iter([1, 2, 3])           # list_iterator
it2 = iter({'a': 1}.values())   # dict_valueiterator
it3 = iter('abc')               # str_iterator
it4 = reversed([1, 2, 3])       # reverse_iterator

print(f'list_iterator это Iterator: {isinstance(it1, Iterator)}')      # True
print(f'list_iterator это Generator: {isinstance(it1, Generator)}')    # False
print(f'\ngen это Iterator: {isinstance(gen1, Iterator)}')             # True
print(f'gen это Generator: {isinstance(gen1, Generator)}')            # True

9. Визуальное сравнение

┌─────────────────────────────────────┐
│ Итерируемые объекты (Iterable)      │
│ (__iter__ возвращает Iterator)      │
│                                     │
│  ┌──────────────────────────────┐   │
│  │ Итераторы (Iterator)         │   │
│  │ (__iter__ и __next__)        │   │
│  │                              │   │
│  │  ┌────────────────────────┐  │   │
│  │  │ Генераторы (Generator) │  │   │
│  │  │ (yield функции)        │  │   │
│  │  │ (__next__, send, throw)│  │   │
│  │  └────────────────────────┘  │   │
│  └──────────────────────────────┘   │
└─────────────────────────────────────┘

Итоговое сравнение

СвойствоИтераторГенератор
СозданиеКласс с __iter__ и __next__Функция с yield
__next__()ДаДа (автоматически)
__iter__()ДаДа (автоматически)
send()НетДа
throw()НетДа
close()НетДа
Ленивое вычислениеОпциональноДа (по умолчанию)
СостояниеВ атрибутахВ локальных переменных
СложностьБольше кодаМеньше кода
ПамятьМожет быть многоМинимально

Мои рекомендации

  • Используй генераторы по умолчанию — они проще и ленивы
  • Используй итераторы когда нужно сложное состояние или переиспользование
  • Помни про send() — генератор это двусторонний канал
  • Генератор это итератор — можешь передать везде где ожидается Iterator
  • В production — генераторы для файлов, сетей, потоков данных