1.1Kпросмотров
25.7%от подписчиков
4 марта 2026 г.
Score: 1.3K
🔄 Итераторы и генераторы: как for реально работает под капотом Все пишут for x in list, но мало кто знает, что за этим скрывается протокол итератора. Разберём магию: как объекты становятся итерируемыми, как написать свой итератор и почему yield экономит память лучше любого оптимизатора. ❓ Что на самом деле делает for Когда ты пишешь: items = [1, 2, 3] for item in items: print(item) Python делает примерно это: iterator = iter(items) # получаем итератор while True: try: item = next(iterator) # берём следующий элемент print(item) except StopIteration: # когда элементы кончились break ➡️ Любой объект можно скормить for, если у него есть методы iter() или getitem(). Но настоящая магия — в протоколе итератора. 🔍 Протокол итератора: два метода, которые всё решают Чтобы объект стал итерируемым, ему нужны: - iter() — возвращает итератор (обычно self) - next() — возвращает следующий элемент или бросает StopIteration class Counter: def init(self, limit): self.limit = limit self.current = 0 def iter(self): return self # возвращаем себя как итератор def next(self): if self.current >= self.limit: raise StopIteration # сигнал, что всё self.current += 1 return self.current - 1 for x in Counter(5): print(x) # 0 1 2 3 4 ➡️ Вот так for понимает, когда остановиться. Всё честно, без магии. ✅ Свой итератор для бесконечной последовательности Хочешь бесконечный счётчик? Легко: class InfiniteCounter: def init(self, start=0): self.current = start def iter(self): return self def next(self): self.current += 1 return self.current - 1 counter = InfiniteCounter() for i in counter: print(i) if i > 5: break # 0 1 2 3 4 5 ➡️ Бесконечность не предел. Главное — не забыть break, иначе уйдёшь в космос. 🧪 Проблема: писать классы каждый раз лень Согласись, тащить целый класс ради одного итератора — жирно. Тут на сцену выходят генераторы. def counter_gen(limit): for i in range(limit): yield i for x in counter_gen(5): print(x) # 0 1 2 3 4 ➡️ yield превращает функцию в генератор. При каждом вызове next() она выполняется до следующего yield и замораживает состояние. 🚀 Бесконечный генератор в одну строку def infinite_gen(start=0): while True: yield start start += 1 gen = infinite_gen() print(next(gen)) # 0 print(next(gen)) # 1 print(next(gen)) # 2 ➡️ Памяти занимает ровно столько, сколько нужно для хранения текущего состояния. Никаких гигантских списков. 📦 Почему генераторы экономят память: наглядный пример import sys # Список numbers_list = [x for x in range(1_000_000)] print(sys.getsizeof(numbers_list)) # ~8 MB # Генератор numbers_gen = (x for x in range(1_000_000)) print(sys.getsizeof(numbers_gen)) # ~112 bytes ➡️ Список хранит все миллион элементов в памяти. Генератор — только текущее состояние и ссылку на функцию. Разница в тысячи раз. 🔧 Реальный кейс: читаем гигабайтный файл def read_large_file(file_path): with open(file_path, 'r') as f: for line in f: yield line.strip() # Используем for line in read_large_file("huge_log.txt"): if "ERROR" in line: print(line) ➡️ Файл читается построчно, в памяти только одна строка. Можно обрабатывать терабайты логов на ноутбуке с 4GB RAM. ⚡️ Генераторные выражения: list comprehension без памяти # Список (жадный) squares_list = [xx for x in range(1000)] # Генератор (ленивый) squares_gen = (xx for x in range(1000)) print(sum(squares_gen)) # можно посчитать сумму без создания списка ➡️ Круглые скобки вместо квадратных — и миллион элементов не жрут память. 🗣️ Запомни: for - это просто синтаксический сахар над iter() и next() с ловлей StopIteration. Генераторы с yield позволяют писать ленивые последовательности без классов.
1.1K
просмотров
3931
символов
Да
эмодзи
Нет
медиа

Другие посты @pytstart

Все посты канала →
🔄 Итераторы и генераторы: как for реально работает под капо — @pytstart | PostSniper