← Назад к вопросам
Что такое ленивые функции?
2.7 Senior🔥 171 комментариев
#DevOps и инфраструктура#Django
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Ленивые функции
Ленивые функции (lazy functions) — это функции, которые отсрачивают вычисление своего результата до момента, когда он действительно потребуется. Это противоположность жадным функциям (eager evaluation), которые вычисляют результат сразу при вызове.
Основной концепт
Ленивое вычисление (lazy evaluation) — это стратегия, при которой:
- Вычисления откладываются на неопределённое время
- Результат вычисляется только когда необходимо
- Может снижать нагрузку и улучшать производительность
- Позволяет работать с бесконечными последовательностями
Примеры ленивых вычислений в Python
1. Генераторы — основной механизм ленивости
# Жадное вычисление — вся сразу в памяти
res_eager = [x ** 2 for x in range(1000000)]
print(f"Занято памяти: {len(res_eager)} элементов")
# Ленивое вычисление — вычисляется по требованию
def lazy_squares():
for x in range(1000000):
yield x ** 2 # Ключевое слово yield
res_lazy = lazy_squares()
for i, val in enumerate(res_lazy):
if i == 5:
break
print(val) # Только 6 элементов обработано, остальное не вычислено
2. Встроенные ленивые функции
# map() — ленивая функция
data = [1, 2, 3, 4, 5]
mapped = map(lambda x: x ** 2, data)
print(type(mapped)) # <class map> — это итератор, не список!
# Вычисляется только при итерации
for val in mapped:
print(val) # 1, 4, 9, 16, 25
# filter() — тоже ленивая
filtered = filter(lambda x: x > 2, [1, 2, 3, 4, 5])
print(next(filtered)) # 3 — вычислено одно значение
# zip() — тоже ленивая
z = zip([1, 2, 3], ["a", "b", "c"])
print(list(z)) # [(1, "a"), (2, "b"), (3, "c")]
3. Декоратор для ленивого вычисления
def lazy_property(func):
"""Ленивое свойство — вычисляется только при первом обращении"""
cache_attr = f"_lazy_{func.__name__}"
@property
def wrapper(self):
if not hasattr(self, cache_attr):
setattr(self, cache_attr, func(self))
return getattr(self, cache_attr)
return wrapper
class DataProcessor:
def __init__(self, data):
self.data = data
@lazy_property
def processed_data(self):
"""Дорогостоящее вычисление"""
print("Вычисляю обработку...")
return [x ** 2 for x in self.data]
processor = DataProcessor([1, 2, 3, 4, 5])
print("Объект создан")
print(processor.processed_data) # Вычисляется ЗДЕСЬ
print(processor.processed_data) # Берётся из кэша, вычисления не повторяются
4. itertools — удобный инструмент
import itertools
# Бесконечная последовательность — невозможна с жадным вычислением!
def infinite_counter():
count = 0
while True:
yield count
count += 1
# Берём только первые 5 элементов
for val in itertools.islice(infinite_counter(), 5):
print(val) # 0, 1, 2, 3, 4
# Комбинирование ленивых операций
result = itertools.islice(
map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, range(100))),
10
)
print(list(result)) # [0, 4, 16, 36, 64, 100, 144, 196, 256, 324]
Практические примеры
Чтение большого файла
def read_large_file(filepath, chunk_size=1024):
"""Ленивое чтение файла — в памяти одновременно только один чанк"""
with open(filepath) as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
# Использование
for chunk in read_large_file("/path/to/huge/file.txt"):
process(chunk) # Обрабатываем по частям
Работа с API страницами
def paginated_api_call(api_url, per_page=100):
"""Ленивая загрузка данных со страницами"""
page = 1
while True:
response = requests.get(f"{api_url}?page={page}&per_page={per_page}")
data = response.json()
if not data:
break
for item in data:
yield item
page += 1
# Обрабатываем данные по мере их загрузки
for item in paginated_api_call("https://api.example.com/items"):
print(item)
Преимущества и недостатки
Преимущества:
- Экономия памяти для больших наборов данных
- Возможность работать с бесконечными последовательностями
- Лучшая производительность — вычисляется только необходимое
- Позволяет строить цепочки операций без промежуточных списков
Недостатки:
- Сложнее отлаживать — неясно когда произойдёт вычисление
- Может быть медленнее для маленьких данных (overhead от итератора)
- Сложнее предсказать время выполнения
- Может вызвать ошибки далеко от причины (deferred exceptions)
Сравнение: жадное vs ленивое
# Жадное (list comprehension)
result_eager = [x ** 2 for x in range(1000000) if x % 2 == 0]
print(len(result_eager)) # 500000 элементов в памяти!
# Ленивое (generator expression)
result_lazy = (x ** 2 for x in range(1000000) if x % 2 == 0)
print(type(result_lazy)) # <class generator> — почти не занимает памяти
print(next(result_lazy)) # 0 — первый элемент вычислен
Ленивые функции — это неотъемлемая часть Python, делающая язык мощным и гибким инструментом для работы с большими объёмами данных.