Какая разница между генераторами и массивами?
Ну собственно именно то, что написано.
"Генератор позволяет" не значит "генератор гарантированно сэкономит память, ужав миллион строк в одну". Это значит, что если есть возможность не создавать массив в памяти перед тем как перебирать его значения, то можно написать функцию-генератор, результат которой будет выглядеть как массив, но при этом будет выдавать по одному значению. Это возможно в таких случаях, как генерация значений на лету или чтение данных из внешнего источника.

То есть фактически это просто красивая обертка над обычным циклом, которая может представить его как "перебор массива".  Эта обертка нам будет нужна, если по каким-то причинам мы хотим обращаться к результатам цикла, как к массиву.

То есть надо чётко понимать, что "экономит память" генератор только по сравнению с созданием полноценного массива. 

Там где вместо создания массива можно обойтись циклом, можно использовать и генератор. Но можно и не использовать - код будет прекрасно работать и без него.
Там, где реально нужен массив, например с произвольным доступом или для накопления результатов вычислений, генератор превращается в тыкву
Разница не в использовании памяти в принципе. А в использовании оперативной памяти, которой меньше.
Рассмотрим кейс: у нас есть набор записей общим объемом 1ТиБ на винте, надо перебрать все и посчитать, скажем, среднее.
Если у нас есть 1ТиБ свободной оперативы - мы можем целиком загрузить все туда и прямым доступом перебрать массив.
Но у нас вряд ли есть столько оперативы. В таком случае мы можем считывать данные блоками размером с доступную оперативную память, выполнять действие, выгружать блок и загружать следующий. 
Генераторы же - синтаксический сахар для таких операций. Можно написать функцию-генератор, которая будет при каждом вызове возвращать следующий элемент. Но не хранить все их в оперативе. Отсюда и экономия памяти, но именно оперативной.