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

Зачем нужен итератор?

2.0 Middle🔥 121 комментариев
#Python Core

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

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

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

Зачем нужен итератор

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

Что такое итератор

Итератор — это объект, который реализует два метода:

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

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

# Или явно через next()
counter = CountIterator(3)
print(next(counter))  # 1
print(next(counter))  # 2
print(next(counter))  # 3
# next(counter)  # StopIteration exception

Основные преимущества итераторов

1. Экономия памяти

Итератор не хранит все элементы в памяти, а генерирует их по требованию.

# ❌ Плохо — весь список в памяти
data = [i for i in range(1_000_000)]  # ~40 МБ
for item in data:
    process(item)

# ✅ Хорошо — итератор генерирует по одному
data = (i for i in range(1_000_000))  # Минимум памяти
for item in data:
    process(item)

2. Ленивое вычисление

Элементы вычисляются только когда они нужны, а не заранее.

# ❌ Все вычисляется сразу
result = [x ** 2 for x in range(10)]
print(result[0])  # Но мы используем только первый!

# ✅ Вычисляется только нужное
result = (x ** 2 for x in range(10))
print(next(result))  # Вычислено только одно значение

3. Работа с бесконечными последовательностями

# Бесконечный итератор
class InfiniteCounter:
    def __init__(self, start=0):
        self.current = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        self.current += 1
        return self.current

# Использование
counter = InfiniteCounter()
for i, num in enumerate(counter):
    if i >= 5:
        break
    print(num)  # 1, 2, 3, 4, 5

# Со списком это невозможно

4. Цепочка трансформаций

# Сложная трансформация без итераторов
data = [1, 2, 3, 4, 5]
data = [x * 2 for x in data]        # Создаёт список
data = [x for x in data if x > 4]   # Создаёт список
data = [str(x) for x in data]       # Создаёт список

# С итераторами — никаких промежуточных списков
data = [1, 2, 3, 4, 5]
result = map(lambda x: x * 2, data)  # Итератор
result = filter(lambda x: x > 4, result)  # Итератор
result = map(str, result)  # Итератор
list(result)  # ["6", "8", "10"] — только один список в конце

Встроенные итераторы в Python

map() — преобразование элементов

data = [1, 2, 3, 4, 5]
squared = map(lambda x: x ** 2, data)
print(next(squared))  # 1
print(next(squared))  # 4
print(next(squared))  # 9

# Или в цикле
for value in squared:
    print(value)

filter() — фильтрация элементов

data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = filter(lambda x: x % 2 == 0, data)

for value in evens:
    print(value)  # 2, 4, 6, 8, 10

zip() — объединение последовательностей

names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]

for name, age in zip(names, ages):
    print(f"{name}: {age}")  # Alice: 25, Bob: 30, ...

enumerate() — с индексом

for i, item in enumerate(['a', 'b', 'c']):
    print(f"{i}: {item}")  # 0: a, 1: b, 2: c

reversed() — в обратном порядке

for item in reversed([1, 2, 3, 4, 5]):
    print(item)  # 5, 4, 3, 2, 1

Модуль itertools — продвинутые итераторы

import itertools

# cycle() — бесконечный цикл
colors = itertools.cycle(['red', 'green', 'blue'])
for i, color in enumerate(colors):
    if i >= 6:
        break
    print(color)  # red, green, blue, red, green, blue

# chain() — объединение итераторов
data1 = [1, 2, 3]
data2 = [4, 5, 6]
for item in itertools.chain(data1, data2):
    print(item)  # 1, 2, 3, 4, 5, 6

# combinations() — все комбинации
for combo in itertools.combinations([1, 2, 3], 2):
    print(combo)  # (1,2), (1,3), (2,3)

# permutations() — все перестановки
for perm in itertools.permutations([1, 2, 3], 2):
    print(perm)  # (1,2), (1,3), (2,1), ...

# groupby() — группировка
data = [1, 1, 2, 2, 2, 3, 3]
for key, group in itertools.groupby(data):
    print(f"{key}: {list(group)}")  # 1: [1,1], 2: [2,2,2], 3: [3,3]

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

1. Чтение больших файлов

# Без итератора — весь файл в памяти
with open('huge_file.txt') as f:
    lines = f.readlines()  # Может быть 1 ГБ
    for line in lines:
        process(line)

# С итератором — по одной строке
with open('huge_file.txt') as f:
    for line in f:  # f — это итератор
        process(line)

2. Обработка данных из БД

from sqlalchemy import select

# ❌ Неправильно — вся БД в памяти
users = db.query(User).all()
for user in users:
    process(user)

# ✅ Правильно — по одному пользователю
for user in db.query(User).yield_per(100):  # Итератор по 100 записей
    process(user)

3. Асинхронная обработка

async def fetch_data():
    for page in range(1, 100):
        data = await fetch_page(page)
        yield data  # Асинхронный итератор

async for batch in fetch_data():
    process(batch)

4. Streaming HTTP ответов

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()

def generate():
    for i in range(1000):
        yield f"data: {i}\n"

@app.get("/stream")
async def stream():
    return StreamingResponse(generate(), media_type="text/event-stream")

Пользовательский итератор

class FileReader:
    """Итератор для чтения файла по частям"""
    def __init__(self, filename, chunk_size=1024):
        self.filename = filename
        self.chunk_size = chunk_size
        self.file = None
    
    def __iter__(self):
        self.file = open(self.filename, 'rb')
        return self
    
    def __next__(self):
        chunk = self.file.read(self.chunk_size)
        if chunk:
            return chunk
        else:
            self.file.close()
            raise StopIteration

# Использование
for chunk in FileReader('large_file.bin', chunk_size=8192):
    process(chunk)  # Обрабатываем по 8 КБ

Отличие от списков

# Список — все в памяти, индексирование
data = [1, 2, 3, 4, 5]
print(data[2])  # Работает
for item in data:
    print(item)

# Итератор — по одному, нет индексирования
data = (x for x in range(1, 6))
# print(data[2])  # TypeError — нельзя!
for item in data:  # Только так
    print(item)

# После прохода итератор исчерпан
data = iter([1, 2, 3])
list(data)  # [1, 2, 3]
list(data)  # [] — пусто!

Когда использовать итераторы

# ✅ Используйте итераторы для:
# - Больших наборов данных
# - Потоковой обработки
# - Бесконечных последовательностей
# - Оптимизации памяти
# - Pipeline обработки данных

# ✅ Используйте списки для:
# - Малых наборов данных
# - Когда нужен доступ по индексу
# - Когда нужно переитерировать несколько раз

Итоги

Итератор нужен для:

  1. Экономии памяти — не хранит все данные
  2. Ленивых вычислений — считает только нужное
  3. Работы с большими данными — файлы, БД, API
  4. Цепочки трансформаций — эффективная обработка
  5. Асинхронности — async iterators
  6. Бесконечности — бесконечные последовательности

Итератор — это одна из самых мощных и часто используемых концепций в Python. Без понимания итераторов невозможно писать эффективный код.