Итераторы в Python: подробное объяснение
Итераторы в Python: подробное объяснение
Что такое итератор?
Итератор - это объект, который позволяет последовательно получать элементы из коллекции или потока данных. Он реализует два основных метода:
- __iter__() - возвращает сам итератор
- __next__() - возвращает следующий элемент
Базовый пример создания итератора
class SimpleIterator:
def __init__(self, limit):
self.limit = limit
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < self.limit:
value = self.current
self.current += 1
return value
else:
raise StopIteration
# Использование
my_iter = SimpleIterator(5)
for num in my_iter:
print(num) # 0, 1, 2, 3, 4
Итерируемые объекты vs итераторы
Важно различать:
- Итерируемый объект - объект, который можно использовать в цикле for
- Итератор - объект, который фактически производит итерацию
# Итерируемый объект (но не итератор) my_list = [1, 2, 3] iter_obj = iter(my_list) # получаем итератор из итерируемого объекта print(next(iter_obj)) # 1 print(next(iter_obj)) # 2 print(next(iter_obj)) # 3 # print(next(iter_obj)) # StopIteration
Встроенные итераторы и итерируемые объекты
Python имеет множество встроенных итерируемых типов:
- Списки, кортежи, строки
- Словари, множества
- Файлы
- Функции range(), enumerate(), zip(), map(), filter()
# Примеры использования
# 1. Файлы как итераторы
with open('file.txt') as f:
for line in f: # файл - итератор строк
print(line)
# 2. enumerate() возвращает итератор пар (индекс, значение)
for i, value in enumerate(['a', 'b', 'c']):
print(f"Index: {i}, Value: {value}")
# 3. zip() объединяет несколько итераторов
names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]
for name, age in zip(names, ages):
print(f"{name} is {age} years old")
Генераторы как частный случай итераторов
Генераторы - это специальный тип итераторов, создаваемых с помощью функций с yield или генераторных выражений.
Функции-генераторы
def count_down(n):
while n > 0:
yield n
n -= 1
# Использование
for num in count_down(5):
print(num) # 5, 4, 3, 2, 1
Генераторные выражения
# Аналогично list comprehension, но создает генератор squares = (x**2 for x in range(10)) print(next(squares)) # 0 print(next(squares)) # 1 # ... # Экономит память - не хранит все элементы сразу
Кастомные итераторы с состоянием
Итераторы могут иметь сложное внутреннее состояние:
class FibonacciIterator:
def __init__(self, max_count):
self.max_count = max_count
self.count = 0
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
if self.count >= self.max_count:
raise StopIteration
if self.count == 0:
result = self.a
elif self.count == 1:
result = self.b
else:
result = self.a + self.b
self.a, self.b = self.b, result
self.count += 1
return result
# Генерация первых 10 чисел Фибоначчи
fib_iter = FibonacciIterator(10)
print(list(fib_iter)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Бесконечные итераторы
import itertools
# 1. Бесконечный счетчик
counter = itertools.count(start=10, step=2)
print(next(counter)) # 10
print(next(counter)) # 12
# ... и так до бесконечности
# 2. Циклический итератор
cycle_iter = itertools.cycle(['A', 'B', 'C'])
print(next(cycle_iter)) # A
print(next(cycle_iter)) # B
print(next(cycle_iter)) # C
print(next(cycle_iter)) # A (снова)
# 3. Повторение элемента
repeater = itertools.repeat('Hello', 3) # только 3 раза
print(list(repeater)) # ['Hello', 'Hello', 'Hello']
Практические применения итераторов
1. Ленивые вычисления
def read_large_file(filename):
"""Чтение большого файла без загрузки в память целиком"""
with open(filename) as f:
for line in f:
yield line.strip()
# Обработка файла построчно
for line in read_large_file('huge_file.txt'):
process_line(line)
2. Конвейерная обработка данных
def pipeline(data):
"""Конвейер обработки данных"""
# Каждый шаг - генератор
step1 = (x * 2 for x in data)
step2 = (x + 10 for x in step1)
step3 = (x for x in step2 if x % 3 == 0)
return step3
result = pipeline(range(100))
print(list(result)) # Все вычисления ленивые
3. Собственные коллекции
class TreeNode:
def __init__(self, value):
self.value = value
self.children = []
def add_child(self, child):
self.children.append(child)
def __iter__(self):
"""Итерация по значениям в дереве (обход в глубину)"""
yield self.value
for child in self.children:
yield from child
# Создание дерева
root = TreeNode(1)
child1 = TreeNode(2)
child2 = TreeNode(3)
root.add_child(child1)
root.add_child(child2)
print(list(root)) # [1, 2, 3]
Полезные функции для работы с итераторами
import itertools
# 1. Цепочка итераторов
chain_iter = itertools.chain([1, 2], 'ab', (4, 5))
print(list(chain_iter)) # [1, 2, 'a', 'b', 4, 5]
# 2. Срезы итераторов
slice_iter = itertools.islice(range(100), 10, 20, 2)
print(list(slice_iter)) # [10, 12, 14, 16, 18]
# 3. Группировка
data = sorted(['apple', 'banana', 'cherry', 'date'], key=len)
for key, group in itertools.groupby(data, key=len):
print(f"Length {key}: {list(group)}")
# 4. Комбинации
print(list(itertools.combinations('ABC', 2))) # [('A', 'B'), ('A', 'C'), ('B', 'C')]
print(list(itertools.permutations('AB', 2))) # [('A', 'B'), ('B', 'A')]
Особенности и ограничения
-
Одноразовость: большинство итераторов можно пройти только один раз
numbers = iter([1, 2, 3]) list(numbers) # [1, 2, 3] list(numbers) # [] - итератор исчерпан
-
Ленивые вычисления: элементы вычисляются по мере необходимости
-
Экономия памяти: не требуют хранения всех данных одновременно
-
Не поддерживают индексацию: нельзя получить элемент по индексу как в списке
Полезные паттерны
Итератор с сохранением состояния
class CheckpointIterator:
def __init__(self, data):
self.data = data
self.index = 0
self.checkpoints = []
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
value = self.data[self.index]
self.index += 1
return value
def checkpoint(self):
self.checkpoints.append(self.index)
def restore(self, checkpoint_idx=-1):
if checkpoint_idx < len(self.checkpoints):
self.index = self.checkpoints[checkpoint_idx]
Заключение
Итераторы в Python - мощный инструмент для:
- Эффективной работы с данными (экономия памяти)
- Ленивых вычислений
- Создания конвейеров обработки
- Работы с бесконечными или большими последовательностями
Они являются фундаментальной частью Python и лежат в основе многих встроенных конструкций языка, включая циклы for и генераторные выражения.
Дополнительный материал
Вопросы учеников к ИИ-помощнику и ответы по теме урока
Привет! Функция reversed() в Python возвращает итератор, который позволяет перебирать последовательность в обратном порядке. Она не изменяет исходную последовательность, а создает новый итератор для обратного обхода.
Вот ключевые моменты:
- Назначение: Создает итератор для обратного обхода итерируемого объекта (строки, кортежа, списка и т.д.).
- Синтаксис:
reversed(sequence) - Возвращаемое значение: Итератор, выдающий элементы последовательности в обратном порядке.
-
Пример:
python my_string = "hello" reversed_string = reversed(my_string) for char in reversed_string: print(char) # Выведет: o, l, l, e, hpython my_list = [1, 2, 3, 4, 5] reversed_list = reversed(my_list) print(list(reversed_list)) # Выведет: [5, 4, 3, 2, 1]
Упражнение:
Напишите функцию, которая принимает список чисел и возвращает новый список, содержащий эти числа в обратном порядке, используя reversed().
Привет! Помимо reversed(), в Python есть несколько других встроенных функций, которые возвращают итераторы, позволяя эффективно работать с данными. Они особенно полезны, когда нужно обрабатывать большие объемы информации, не загружая их все в память сразу.
Вот некоторые из них:
-
range(): Создает последовательность чисел.range(stop): от 0 доstop(не включаяstop).range(start, stop): отstartдоstop(не включаяstop).range(start, stop, step): отstartдоstopс шагомstep.- Пример:
for i in range(5): print(i)(выведет 0, 1, 2, 3, 4)
-
enumerate(): Добавляет счетчик к итерируемому объекту и возвращает итератор кортежей (индекс, элемент).- Пример:
for i, char in enumerate("abc"): print(f"Index: {i}, Char: {char}")(выведет Index: 0, Char: a, Index: 1, Char: b, Index: 2, Char: c)
- Пример:
-
zip(): Объединяет несколько итерируемых объектов в один итератор кортежей.- Пример:
names = ['a', 'b']; ages = [1, 2]; for name, age in zip(names, ages): print(f"{name} is {age}")(выведет a is 1, b is 2)
- Пример:
-
map(): Применяет функцию к каждому элементу итерируемого объекта и возвращает итератор результатов.- Пример:
numbers = [1, 2, 3]; squared = map(lambda x: x**2, numbers); print(list(squared))(выведет [1, 4, 9])
- Пример:
-
filter(): Фильтрует элементы итерируемого объекта на основе заданной функции и возвращает итератор отфильтрованных элементов.- Пример:
numbers = [1, 2, 3, 4, 5]; even = filter(lambda x: x % 2 == 0, numbers); print(list(even))(выведет [2, 4])
- Пример:
Упражнение:
Используя функцию zip(), создайте функцию, которая принимает два списка (имена и фамилии) и возвращает список строк, содержащих полное имя (имя + пробел + фамилия).