Генераторы в классах: yield внутри методов
Когда обычных методов класса недостаточно ⚡
Представь ситуацию: ты пишешь класс для обработки большого потока данных. Обычные методы с return возвращают всё сразу — но что если данных миллионы? Загружать их в память целиком неэффективно. Вот где на помощь приходят генераторы с yield внутри методов класса!
Генераторы в классах — это мощный инструмент, который позволяет:
- Экономить память за счёт ленивых вычислений 🦥
- Создавать последовательности "на лету" ✈️
- Делать код чище и читабельнее 🧼
Основы: делаем метод-генератор 🏗️
Создать генератор в классе проще простого — достаточно использовать yield вместо return:
class DataProcessor:
def __init__(self, data):
self.data = data
def process(self):
for item in self.data:
yield item * 2 # Обрабатываем элементы по одному!
processor = DataProcessor([1, 2, 3])
for result in processor.process():
print(result)
# Вывод: 2, 4, 6
Ключевые моменты:
- Метод
process()теперь генератор (из-заyield) - При вызове он возвращает генератор, а не готовый список
- Данные обрабатываются поэлементно по мере необходимости
Реальный пример: чтение больших файлов 📚
Давай рассмотрим практический пример с обработкой CSV-файлов:
import csv
class CSVReader:
def __init__(self, filename):
self.filename = filename
def read_rows(self):
with open(self.filename, 'r') as file:
reader = csv.reader(file)
for row in reader:
yield row # Постепенно читаем файл
# Использование:
data_reader = CSVReader('huge_dataset.csv')
for row in data_reader.read_rows():
process_row(row) # Обрабатываем каждую строку отдельно
Преимущества подхода:
- Файл читается построчно, а не целиком 🎯
- Потребление памяти постоянно, даже для огромных файлов 🐘
- Можно прервать обработку в любой момент ⏸️
Состояние генератора в классах 🧭
Генераторы в методах прекрасно работают с атрибутами класса:
class Paginator:
def __init__(self, items, per_page=10):
self.items = items
self.per_page = per_page
def paginate(self):
for i in range(0, len(self.items), self.per_page):
yield self.items[i:i + self.per_page]
# Использование:
pages = Paginator(list(range(100)), 15)
for page in pages.paginate():
print(f"Страница: {page}")
Здесь мы:
- Используем атрибуты класса (
items,per_page) внутри генератора - Сохраняем состояние между вызовами
yield - Создаём удобный интерфейс для постраничного вывода
Комбинируем с другими методами класса 🎲
Генераторы отлично сочетаются с обычными методами:
class Fibonacci:
def __init__(self, limit):
self.limit = limit
def generate(self):
a, b = 0, 1
for _ in range(self.limit):
yield a
a, b = b, a + b
def sum(self):
return sum(self.generate()) # Используем генератор внутри метода
fib = Fibonacci(10)
print(list(fib.generate())) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
print(fib.sum()) # 88
Такой подход делает классы:
- Гибкими: можно использовать генерацию и обычные методы
- Эффективными: не храним всю последовательность в памяти
- Удобными: разные способы работы с данными в одном месте
Когда стоит использовать yield в методах? 🤔
Генераторы в классах особенно полезны, когда:
- Работаешь с большими или бесконечными последовательностями 🌌
- Нужно экономить память (ленивые вычисления) 💾
- Хочешь сделать код более читаемым и декларативным 📖
- Работаешь с потоками данных или пошаговой обработкой 🏗️
Но помни: если тебе нужно сразу все результаты — возможно, обычный метод с return будет лучше.
Главное на практике 🎯
- Генераторы в методах создаются через
yield - Они сохраняют контекст класса и его атрибуты
- Отлично сочетаются с другими методами класса
- Идеальны для обработки больших данных
Теперь ты готов создавать эффективные и элегантные классы с генераторами! Попробуй применить этот подход в своём следующем проекте — результат тебя приятно удивит. 😊