🔄 Генераторы и yield: как делать ленивые вычисления и экономить память
Обычные функции в Python возвращают всё сразу. Генераторы — по частям. Это «ленивые» вычисления: результат отдаётся только тогда, когда он нужен. Экономия памяти и скорость работы на больших данных становятся колоссальными.
⚡️ Простейший пример с yield
def numbers():
yield 1
yield 2
yield 3
for n in numbers():
print(n)
➡️ Функция не возвращает список, а отдаёт значения по одному.
📂 Чтение больших файлов построчно
def read_file(path):
with open(path, "r") as f:
for line in f:
yield line.strip()
for line in read_file("big.log"):
print(line)
➡️ Файл читается строчка за строчкой, без загрузки всего в память.
♾️ Бесконечные последовательности
def counter(start=0):
while True:
yield start
start += 1
for n in counter():
if n > 5:
break
print(n)
➡️ Генератор может выдавать элементы бесконечно. Список так сделать нельзя.
🔗 Пайплайны генераторов
def numbers():
for i in range(10):
yield i
def squared(seq):
for x in seq:
yield x * x
for x in squared(numbers()):
print(x)
➡️ Одни генераторы передают данные другим. Получается конвейер обработки.
🛠 Генераторные выражения
squares = (x*x for x in range(1_000_000))
print(next(squares)) # 1
print(next(squares)) # 4
➡️ Почти как list comprehension, но без хранения всех элементов в памяти.
🟢 Где это реально нужно:
🗣️ Запомни:yield отдаёт результат по частям, не занимая память. Генераторы можно объединять в цепочки — как трубы. Если данных много или они бесконечны — только генераторы спасут.
Обычные функции в Python возвращают всё сразу. Генераторы — по частям. Это «ленивые» вычисления: результат отдаётся только тогда, когда он нужен. Экономия памяти и скорость работы на больших данных становятся колоссальными.
⚡️ Простейший пример с yield
def numbers():
yield 1
yield 2
yield 3
for n in numbers():
print(n)
➡️ Функция не возвращает список, а отдаёт значения по одному.
📂 Чтение больших файлов построчно
def read_file(path):
with open(path, "r") as f:
for line in f:
yield line.strip()
for line in read_file("big.log"):
print(line)
➡️ Файл читается строчка за строчкой, без загрузки всего в память.
♾️ Бесконечные последовательности
def counter(start=0):
while True:
yield start
start += 1
for n in counter():
if n > 5:
break
print(n)
➡️ Генератор может выдавать элементы бесконечно. Список так сделать нельзя.
🔗 Пайплайны генераторов
def numbers():
for i in range(10):
yield i
def squared(seq):
for x in seq:
yield x * x
for x in squared(numbers()):
print(x)
➡️ Одни генераторы передают данные другим. Получается конвейер обработки.
🛠 Генераторные выражения
squares = (x*x for x in range(1_000_000))
print(next(squares)) # 1
print(next(squares)) # 4
➡️ Почти как list comprehension, но без хранения всех элементов в памяти.
🟢 Где это реально нужно:
🟢обработка логов и CSV гигабайтного размера;
🟢потоковые данные (сокеты, API);
🟢построение бесконечных последовательностей;
🟢конвейеры обработки (map/filter без лишних списков).
🗣️ Запомни:yield отдаёт результат по частям, не занимая память. Генераторы можно объединять в цепочки — как трубы. Если данных много или они бесконечны — только генераторы спасут.