Про генераторы и итераторы Мне когда-то советовали прочитать книжку Лутца по питону, но я ее забросила. Теперь мне нечего было делать, и я снова решила к ней вернуться) И тут я как осознала! 📝 Сделаем одно упражнение. Возьмем список, отберем только четные числа двумя способами: ```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)) # <class 'list'> print(type(l2)) # <class 'generator'>``` А теперь дважды материализуем: ```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 итераторов может быть много, как будто они передаются по значению. А в остальных случаях как будто по ссылке - когда изменяется одна, изменяется и другая. По сути в тех случаях, когда нам не нужно несколько раз обращаться к одним данным, мы спокойно можем использовать генератор✅