Почему не хочется создавать большой список при фильтрации в Python
Что проверяют
Понимаете ли вы, за что платите памятью, и знаете ли ленивые инструменты языка. Заодно смотрят, не превратились ли генераторы в карго-культ: в хорошем ответе есть и обратная сторона.
Как отвечать
Список материализует все отобранные элементы сразу. Сам он массив указателей, по восемь байт на элемент на 64-битной сборке, плюс объекты, на которые они смотрят. Миллион небольших целых занимает не восемь мегабайт, а десятки: у каждого объекта свой заголовок, и sys.getsizeof это показывает.
Генераторное выражение хранит только текущий элемент и состояние обхода:
total = sum(row.amount for row in rows if row.is_paid)Память здесь константная, а первый результат доступен сразу, а не после прохода всего источника. Это важно, когда нужен не весь отбор, а первое совпадение или первые несколько: next() и itertools.islice останавливают работу, список заставляет довести её до конца. Генераторы соединяются в конвейер, и данные идут по одному элементу через все шаги, без промежуточного списка на каждом.
Обязательно назовите, когда список всё-таки нужен: несколько проходов, len(), индексация, сортировка, передача в библиотеку, ожидающую последовательность. Генератор одноразовый. А для числовых данных правильный ответ чаще не генератор, а массив NumPy: векторизованная маска обгоняет поэлементный цикл интерпретатора.
Где спотыкаются
Говорят, что генератор быстрее. На полном обходе списковое включение обычно не медленнее, а иногда и быстрее: у него нет переключения кадров на каждом элементе. Выигрыш генератора в памяти и в задержке до первого результата, так и формулируйте.
Материализуют список и тут же отдают его в sum, any или min. Лишние скобки не только занимают память: any() умеет остановиться на первом истинном значении, а готовый список этой возможности его лишает.
Забывают, что генератор одноразовый и читает источник лениво. Второй проход даёт пустоту, а если за ним открытый файл или курсор базы, к моменту чтения он может быть уже закрыт, и ошибка вылезет далеко от места создания.