Короткий ответ
Генератор возвращает значения лениво, по одному, по запросу, не храня всю последовательность в памяти, а функция со списком вычисляет и хранит все элементы целиком до возврата.
Как это работает подробнее
При вызове функции с yield Python не выполняет её тело сразу, а возвращает объект-генератор; выполнение кода происходит пошагово при каждом вызове next(), останавливаясь на yield и возобновляясь с этого места при следующем запросе. Функция, строящая список, выполняется от начала до конца за один вызов и возвращает готовый объект.
Пример кода
example-242.py
def gen_squares(n):
for i in range(n):
yield i * i
def list_squares(n):
return [i * i for i in range(n)]
g = gen_squares(5)
print(next(g)) # 0, тело функции выполняется до первого yield
print(list(list_squares(5))) # [0, 1, 4, 9, 16] - полностью готовый список сразуЧто использовать на практике
- Практика. Генераторы экономят память при обработке больших или бесконечных последовательностей (чтение файла построчно, поток данных), но позволяют пройти последовательность только один раз вперёд; если нужен многократный доступ, индексация или
len(), список подходит лучше.
Подводные камни
- Риски. Используют список, когда достаточно один раз пройтись по данным (лишний расход памяти); пытаются индексировать генератор как список (
TypeError) или дважды итерировать уже исчерпанный генератор, получая пустой результат без ошибки.