Про генераторы и итераторы

Мне когда-то советовали прочитать книжку Лутца по питону, но я ее забросила. Теперь мне нечего было делать, и я снова решила к ней вернуться) И тут я как осознала!

📝 Сделаем одно упражнение. Возьмем список, отберем только четные числа двумя способами:

`lst = [1, 2, 3, 4] l1 = [x for x in lst if x % 2 == 0] l2 = (x for x in lst if x % 2 == 0)

print(type(l1))

#

print(type(l2))

#

А теперь дважды материализуем:

`print(list(l1)) print(list(l1))

#[2, 4] #[2, 4]

print(list(l2)) print(list(l2))

#[2, 4] #[]

Список хранит все в памяти, занимает оперативку, и мы можем его вызывать сколько угодно раз. А генератор закончился и всегда будет пустым🔲 ➖➖➖➖

Если мы попробуем сделать то же самое ручками, то есть еще интересный нюанс. Сделаем два итератора - для функции range и нашего генератора l2. Сначала вызовем первый, потом второй:

`r = range(2) i1 = iter(r) i2 = iter(r) print(next(i1))

#0

print(next(i2))

#0 - заново

l2 = (x for x in lst if x % 2 == 0) i1 = iter(l2) i2 = iter(l2) print(next(i1))

#2

print(next(i2))

#4 - продолжили

🌀 Прикол в том, что у range итераторов может быть много, как будто они передаются по значению. А в остальных случаях как будто по ссылке - когда изменяется одна, изменяется и другая.

По сути в тех случаях, когда нам не нужно несколько раз обращаться к одним данным, мы спокойно можем использовать генератор✅```