Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое yield в Python?
yield — это ключевое слово, которое превращает функцию в генератор. Вместо return (которого возвращает результат и завершает функцию), yield возвращает значение и сохраняет состояние функции для продолжения при следующем вызове.
Основной принцип: ленивые вычисления
Обычная функция вычисляет все результаты сразу:
def numbers():
return [1, 2, 3, 4, 5] # создаёт весь список в памяти
for n in numbers():
print(n)
Генератор с yield вычисляет результаты по одному:
def numbers():
yield 1 # сохраняет состояние и возвращает 1
yield 2 # продолжает отсюда, возвращает 2
yield 3
yield 4
yield 5
for n in numbers(): # вызывает функцию, получает по одному значению
print(n)
1. Экономия памяти
Проблема: создание большого списка
def get_large_list(n):
"""Обычная функция — весь список в памяти"""
result = []
for i in range(n):
result.append(i ** 2)
return result
big_list = get_large_list(1000000) # создаёт 1 млн элементов в памяти
Решение: генератор
def get_large_numbers(n):
"""Генератор — по одному числу в памяти"""
for i in range(n):
yield i ** 2
for num in get_large_numbers(1000000): # каждый раз один элемент в памяти
print(num)
2. Как работает yield: пошагово
def simple_generator():
print("Начало")
yield 1 # первый вызов next() возвращает 1
print("После 1")
yield 2 # второй вызов next() возвращает 2
print("После 2")
yield 3 # третий вызов next() возвращает 3
print("Конец")
gen = simple_generator() # функция НЕ выполнялась!
print(next(gen)) # Начало, потом возвращает 1
print(next(gen)) # После 1, потом возвращает 2
print(next(gen)) # После 2, потом возвращает 3
print(next(gen)) # Конец, затем StopIteration
3. Генератор as итератор
def count_up(max):
current = 1
while current <= max:
yield current
current += 1
# Способ 1: for loop
for n in count_up(3):
print(n) # 1, 2, 3
# Способ 2: next()
gen = count_up(3)
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
# print(next(gen)) # StopIteration
# Способ 3: list() — материализовать в список
gen = count_up(3)
nums = list(gen) # [1, 2, 3]
4. Бесконечные последовательности
Генератор идеален для бесконечных потоков данных:
def fibonacci():
"""Бесконечный генератор чисел Фибоначчи"""
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# Получить первые 10 чисел Фибоначчи
fib_gen = fibonacci()
fib_list = [next(fib_gen) for _ in range(10)]
print(fib_list) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
# Или с itertools.islice
from itertools import islice
fib_list = list(islice(fibonacci(), 10))
print(fib_list) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
5. Чтение больших файлов
def read_large_file(filepath, chunk_size=1024):
"""Читает большой файл частями"""
with open(filepath, 'r') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
# Обработка гигабайтного файла без загрузки в памяти
for chunk in read_large_file('huge_file.txt'):
print(f"Обработана часть размером {len(chunk)} символов")
# Более практично — читать строки
def read_lines(filepath):
"""Читает файл построчно"""
with open(filepath, 'r') as f:
for line in f: # файловый объект уже генератор!
yield line.strip()
for line in read_lines('data.txt'):
process_line(line)
6. Конвейеры обработки данных (Pipeline)
def read_data():
"""Источник данных"""
for i in range(1, 6):
print(f"Читаю {i}")
yield i
def filter_even(numbers):
"""Фильтр чётных чисел"""
for n in numbers:
if n % 2 == 0:
print(f"Пропускаю {n}")
continue
print(f"Пропускаю нечётное {n}")
yield n
def double(numbers):
"""Удвоение чисел"""
for n in numbers:
print(f"Удваиваю {n}")
yield n * 2
# Конвейер — ленивые вычисления!
data = read_data()
filtered = filter_even(data)
doubled = double(filtered)
for result in doubled:
print(f"Результат: {result}")
7. send() и throw() — двусторонняя коммуникация
def echo():
"""Генератор, который может принимать значения"""
while True:
value = yield # ожидание значения
print(f"Получено: {value}")
gen = echo()
next(gen) # инициализация генератора (выполняет до первого yield)
gen.send("Hello") # отправляет значение в генератор
gen.send("World")
# gen.close() # закрытие генератора
# Практический пример: обработчик команд
def command_handler():
print("Обработчик команд запущен")
try:
while True:
cmd = yield # ожидание команды
if cmd == 'quit':
print("Завершение")
break
else:
print(f"Обработка: {cmd}")
except GeneratorExit:
print("Генератор закрыт")
handler = command_handler()
next(handler) # инициализация
handler.send('start')
handler.send('process')
handler.close()
8. Практические примеры
Обработка CSV файла:
def parse_csv(filepath):
"""Парсит CSV и возвращает словари"""
with open(filepath, 'r') as f:
header = next(f).strip().split(',')
for line in f:
values = line.strip().split(',')
yield dict(zip(header, values))
for row in parse_csv('data.csv'):
print(row) # {'name': 'Alice', 'age': '30', ...}
API с пагинацией:
import requests
def fetch_paginated_data(url, per_page=100):
"""Получает данные со всех страниц API"""
page = 1
while True:
response = requests.get(url, params={'page': page, 'per_page': per_page})
data = response.json()
if not data:
break # нет больше данных
for item in data:
yield item
page += 1
for user in fetch_paginated_data('https://api.example.com/users'):
print(user['name'])
9. yield from — делегирование
def flatten(nested_list):
"""Flatten вложенного списка"""
for item in nested_list:
if isinstance(item, list):
yield from flatten(item) # yield from делегирует
else:
yield item
nested = [1, [2, 3, [4, 5]], 6]
print(list(flatten(nested))) # [1, 2, 3, 4, 5, 6]
Когда использовать yield:
✅ Используйте:
- Больше данных, чем помещается в памяти
- Обработка потоков (сеть, файлы, БД)
- Бесконечные последовательности
- Конвейеры обработки данных
- Избежание создания временных списков
❌ Не используйте:
- Когда нужно несколько раз пройти по данным (генератор можно использовать только один раз)
- Для простых функций с малым результатом
- Когда индексация необходима (list) vs (generator)
Сравнение памяти:
import sys
# Функция со списком
def list_version(n):
return [i for i in range(n)]
# Генератор
def gen_version(n):
for i in range(n):
yield i
print(sys.getsizeof(list_version(100))) # ~920 байт
print(sys.getsizeof(gen_version(100))) # ~128 байт
print(sys.getsizeof(list_version(1000000))) # ~8.7 МБ
print(sys.getsizeof(gen_version(1000000))) # ~128 байт (всегда!)