Коротко

Генератор — итератор, который лениво выдаёт значения через yield, не строя весь список в памяти.

def countdown(n):
    while n > 0:
        yield n
        n -= 1

for x in countdown(3):
    print(x)

Generator expression: (x * x for x in range(10**6)).

Зачем

Большие потоки данных, пайплайны, бесконечные последовательности.

Короткий ответ

«Функция с yield возвращает генератор: значения считаются по запросу, память не раздувается как у списка.»