Когда вы звоните sq(l1)
, внутри sq
, список y
заполняется. Это потребляет память, размер которой пропорционален размеру x
после исчерпания.
Во втором случае, когда вы звоните sqg(l1)
, sqg
не имеет внутреннего списка, используемого для хранения результатов. Он напрямую выдает вычисленные значения, что делает потребляемую память постоянной и независимой от размера x
после исчерпания.
Что касается преимуществ итераторов, не являющихся генераторами, по сравнению с генераторами, я не думаю, что есть преимущества в производительности, но могут быть и структурные преимущества. Генератор (тип итератора, как вы заметили) определяется как итератор, возвращаемый вызовом функции с yield
инструкциями внутри. Это означает, что вы не можете добавлять какие-либо дополнительные методы, которые могут быть вызваны к объекту, представляющему генератор, поскольку этот специальный тип итератора предоставляется вам неявно.
С другой стороны, итератор имеет более свободное определение:объект с методом __next__
и методом __iter__
, возвращающим self
. Вы можете создать класс Squares
, который соответствует предыдущему критерию для итератора, и для того, чтобы получить экземпляр для этого итератора, вам придется явно создать экземпляр Squares
. Поскольку у вас есть контроль над атрибутами итератора , возвращенными вам, вы можете добавить методы экземпляра, возвращающие внутреннее состояние этого итератора, которые не выражаются через __next__
, тогда как с генератором вы заблокированы вобъект генератора, предоставленный вам неявно. Часто генератор будет выполнять эту работу, но иногда вам нужно использовать итератор, не связанный с генератором, чтобы получить контроль, который вам необходим, за счет функциональности, предоставляемой __next__
.
В этом конкретном случае я не верювам нужен явный контроль, предоставленный вам с помощью итератора без генератора, поэтому было бы лучше использовать генератор.