Какие преимущества у генераторов перед списками в Python?

«Какие преимущества у генераторов перед списками в Python?» — вопрос из категории Python, который задают на 33% собеседований Data Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Основное преимущество — эффективность по памяти и времени старта, так как генераторы лениво вычисляют и выдают элементы по одному, а не хранят всю последовательность в памяти сразу.

Пример, демонстрирующий разницу:

# Список: создается и хранится в памяти целиком
list_squares = [x**2 for x in range(1000000)]  # Занимает десятки мегабайт
print(list_squares[0])  # Доступ есть сразу

# Генераторное выражение: создает итератор
gen_squares = (x**2 for x in range(1000000))  # Занимает минимум памяти
print(next(gen_squares))  # Каждый элемент вычисляется в момент запроса

Ключевые преимущества генераторов:

  1. Экономия памяти: Незаменимы для обработки больших или потенциально бесконечных потоков данных (например, чтение больших файлов построчно, обработка сетевых потоков).
  2. Быстрое начало работы: Генератор начинает выдавать элементы немедленно, не тратя время на построение всей коллекции.
  3. Возможность работы с бесконечными последовательностями:

    def infinite_counter():
        i = 0
        while True:
            yield i
            i += 1
    
    counter = infinite_counter()
    print(next(counter))  # 0
    print(next(counter))  # 1
    # Список для этого невозможен
  4. Композиция (конвейерная обработка): Генераторы можно объединять в цепочки без создания промежуточных списков, что также экономит память.
    # Конвейер из генераторов
    numbers = (x for x in range(1000000))
    squares = (x**2 for x in numbers)
    even_squares = (x for x in squares if x % 2 == 0)
    # Ни один полный список не создан в памяти