← Назад к вопросам
Как понимаешь фразу, что любой генератор это итератор, но не любой итератор это генератор?
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 — генераторы для файлов, сетей, потоков данных