Чем Генераторы отличаются от Итераторов в Python?

  Рет қаралды 16,289

Хитрый питон

Хитрый питон

3 жыл бұрын

Частый вопрос - в чем разница между генераторами и итераторами? Давайте разберемся на примере!
Как устроены итераторы в Python? • Как устроены итераторы...
Знакомимся с генераторами в Python • Знакомимся с генератор...
Я в Telegram - t.me/tricky_python
Канал создан при поддержке сообщества Moscow Python.
Наш KZfaq-канал - / @moscowdjangoru
Курсы Learn Python - learn.python.ru/

Пікірлер: 33
@amyodov
@amyodov 3 жыл бұрын
Тут всё-таки реально просится упоминание ещё и про Iterable, а не только про Iterator. Iterable и Iterator - это «протоколы», правила. Как именно и что именно ты должен написать в классе, чтобы сделать его «итерируемым». Между ними есть тонюсенькая, но важная разница. Iterable - в переводе, «итерируемый» - это свойство того, по кому будем итерироваться. Представьте, что вы пишете логику хождения по файловой системе, или логику того, как паук-краулер облазивает весь интернет. Iterable, «итерируемой сущностью» в этих случаях будет то, «что итерируемо». Файловая система - итерируема? Да, по ней можно итерироваться. Интернет - итерируем? - да, по нему можно «ползать». Именно он и есть Iterable; именно он и реализует метод __iter__. Iterator - в переводе, это «итерирующий». В нашем случае эти два класса совпадают, и получается слегка запутанно. Но это вовсе не обязательно. Итератор - это «тот, кто ходит». Отслеживает текущее состояние. В терминах БД это часто называется «курсором». Этот объект может отличаться от iterable и должен реализовывать метод __next__. И именно этот объект, вероятно, будет возвращать метод __iter__ из предыдущего абзаца. Итак, всё просто. Iterable - сущность, по которой ходим. Книга, которую листаем. Iterator - листающая сущность. Палец, который указывает на нужный лист в книге. И оба - это всего лишь «наборы правил, как реализовывать итерацию средствами Питона». Генератор же - немножко интереснее. Это не протокол, а конкретная реализация. Это хитрый механизм в Питоне, который позволяет тебе сделать однократно перебираемую функцию, логика перебора которой легко и красиво описывается. Ты в цикле описываешь процесс генерации каждого элемента, но не считаешь каждый последующий элемент до тех пор, пока он тебе не потребуется. А может быть, он и не потребуется вовсе. Этакий «ленивый итератор», в программистском понимании слова «ленивый» (lazy). Ты описал правило формирования каждого элемента - но формируешь его только тогда, когда его запросят (потому что «считать заранее тебе лень»). А главное - способный к шикарной работе, принимая такие же генераторы (или какие-то другие итераторы) на вход. Ими можно описывать даже бесконечные последовательности - а какая разница, всё равно считаться и перебираться они будут только по одному элементу. Но это не «протокол», не правило реализации. А, повторюсь - встроенная в язык конкретная особенность, можно было бы даже сказать «структура данных» (только это не структура хранения данных, а скорее структура алгоритмической работы с ними). «Есть списки, есть словари, а есть итераторы».
@user-th6xg5bk4c
@user-th6xg5bk4c 3 жыл бұрын
Про итератор и iterable я в отдельном видео рассказывал и тут повторять по второму разу не увидел смысла
@user-dp7sv9pj3f
@user-dp7sv9pj3f 2 жыл бұрын
Спасибо за дополнение!)
@rollangrant8347
@rollangrant8347 Жыл бұрын
@@user-th6xg5bk4c а надо было.
@aleksanderm1947
@aleksanderm1947 Ай бұрын
Аминь! Вафлерша на видео ничего полезного не сказала.
@mantrida
@mantrida 3 жыл бұрын
В чем разница между итератором и генератором ? Чтобы написать итератор требуется больше кода! Гениальное объяснение )
@andrewi5853
@andrewi5853 Жыл бұрын
да, тоже удивило это объяснение
@doublehate
@doublehate Жыл бұрын
Итератор - связан с последовательностью и его перебором, генератор - нет, он только генерирует след. значение и возвращает его по мере необходимости
@user-kc6wd2df5t
@user-kc6wd2df5t 10 ай бұрын
@@doublehate А итератор не возвращает по мере необходимости? Функция next!
@SamurayXXI
@SamurayXXI 3 жыл бұрын
Мне кажется тема раскрыта не до конца. Было бы неплохо упомянуть про разницу в используемой памяти и то, что генератор можно обойти только один раз
@user-th6xg5bk4c
@user-th6xg5bk4c 3 жыл бұрын
Видимо имеется ввиду iterable - да, я если честно не стал лезть совсем в детали
@FoodMaks
@FoodMaks 3 жыл бұрын
Полностью согласен, на вопрос чем генератор отличается от итератора давать ответ - что генератор гораздо короче в реализации, в корень не верно. Главное тут количество проходов и как они ведут себя по отношению к памяти. И это совсем не дебри, а именно отличия...
@user-th6xg5bk4c
@user-th6xg5bk4c 3 жыл бұрын
Судя по упоминанию количества проходов - речь все-таки про Iterable. У итератора так-же как у генератора - один проход, после чего StopIteration и реинициализация если нужно повторно итерироваться. По использованию памяти - я в видео показываю пример, где там перерасход?
@FoodMaks
@FoodMaks 3 жыл бұрын
@@user-th6xg5bk4c, в том то и дело, что итератор можно сделать не в один проход. И он будет и iterable и iterator. А про память минимум можно было упомянуть, что данные у генератора не загружаются в память, а генерируются на лету. Вы же разницу показываете.
@dmitriysintsov8348
@dmitriysintsov8348 3 жыл бұрын
@@user-th6xg5bk4c Как я понимаю можно было дальше развить пример от итераторов к генераторам и от генераторов к async / await. Обобщить все.
@86Blind
@86Blind 2 жыл бұрын
Было интересно 👍
@userunknown545
@userunknown545 10 ай бұрын
еще можно преобразовать объект в итератор при помощи iter() и пройтись по нему, используя next()
@user-dy4nj1cd2d
@user-dy4nj1cd2d 3 жыл бұрын
Попытался представить зачем мне может понадобиться писать итератор и сходу не придумал. Вероятно для более сложных схем. Например итератор может уметь в len. По идее он многоразовый, но сама процедура перезапуска вызывает вопросы, будет ли она консистентна при попытках асинхронного и многопоточного итерирования. Так что в повседневной практике наверное особо нет смысла писать свои итераторы...
@user-th6xg5bk4c
@user-th6xg5bk4c 3 жыл бұрын
Да, куда проще обычно использовать yield
@amyodov
@amyodov 3 жыл бұрын
Я рядом (в соседнем комментарии) обсудил/подумал вслух про это немножко подробнее. Но там было скорее про различия, а можно отдельно обсудить, «зачем». В целом итератор может захотеться написать тогда,... а когда его нет, но он просится. Вы пишете какую-то логику, и ей явно просится возможность «итерирования по данным». Наглядные примеры - пишете ORM/обёртку для работы с БД; у вас наверняка будет какой-то класс `SQLResponse`, и в нём может быть... очень много строк. Настолько много, что в памяти они все не уберутся. Да и не надо. Т.к. SQLResponse, который вы пишете, наверняка сам будет использоваться «для итерирования по нему» - `for row in query.send(): # query.send returns SQLResponse` , то вам наверняка захочется описать `SQLResponse` как Iterable (реализовать в нём __iter__). А поскольку получать каждый следующий элемент вы будете через работу с DB-шным курсором - то именно сам класс, в котором инкапсулирована логика «курсора», может быть отдельным. Какой-нибудь `SQLResponse.Cursor`. И уже он будет реализовывать метод `__next__`. А вообще задач много. Где надо итерироваться - там хочется реализовать Iterable. Где надо итерировать - там хочется реализовать Iterator. И это далеко не одно и то же. Вот напишите, например, класс `Tree`, «дерево» (которое вариант графа). Дерево итерируемо? - итерируемо. У него наверняка будет метод «обойти в ширину». И будет (другой!) метод «обойти в глубину». И метод `__iter__` будет, для простоты, дёргать один этих методов (какой? - да какая разница). Дерево итерируемо аж двумя способами. Но у одного _итерируемого_ дерева (`Tree`) могут быть аж два _итератора_. `Tree.DepthFirstIterator` (класс!) и `Tree.BreadthFirstIterator` (класс!). Вот и разница. ... но если не хотите для «обхода в глубину» и «обхода в ширину» делать два различных класса - то и не надо. Людям чаще нужны дырки, а не дрели. И людям чаще нужны Iterable, а не Iterator. И если в классе Tree вы реализуете `def __iter__(self): return self.iter_breadth_first()`, и реализуете два метода `def iter_breadth_first(self)` и `def iter_depth_first(self)` - но эти методы не будут использовать промежуточных классов «итераторов», а будут обеспечивать итерируемость, например, с помощью алгоритмов на базе генераторов - то большинству этого хватит за глаза. Но иногда написать выделенный класс «итератора» может быть просто архитектурно удобнее.
@user-dy4nj1cd2d
@user-dy4nj1cd2d 3 жыл бұрын
@@amyodov пример с деревом хорош. Я бы в этом случае реализовал два этих итератора отдельными классами, чтобы код самого дерева вообще не трогать.
@Zulya6767
@Zulya6767 3 жыл бұрын
Здравствуйте, а можете пожалуйста сделать видео по разбору основных библиотек пайтона и работу с графическим интерфейсом. Просто эта тема как-то плохо мне даётся
@user-th6xg5bk4c
@user-th6xg5bk4c 3 жыл бұрын
Привет! С gui я работал мало. Подумаю: что можно сделать но быстро не обещаю :)
@Zulya6767
@Zulya6767 3 жыл бұрын
@@user-th6xg5bk4c спасибо :)
@user-uk2dk6bb9j
@user-uk2dk6bb9j Жыл бұрын
Не совсем понял, а зачем собственно писать целый класс для итерации по строке? Если можно просто проитерироваться по строке в цикле for и возвращать тоже самое что в генераторе, только без создания самого генератора)
@alexey6680
@alexey6680 Жыл бұрын
Слышал где-то, что функция len бесплатная(не расходует ресурсы)
@doublehate
@doublehate Жыл бұрын
По сути да, у каждого объекта есть поле ob_size и т.д.(где то по разному именуются) и функция len просто возвращает значение этого поля
@user-dw6vd9xf7r
@user-dw6vd9xf7r 2 жыл бұрын
2:29 Пазишн намбер уан - отдыхаю сам )
@eugenebybin6403
@eugenebybin6403 Жыл бұрын
В классе итератора ошибка, он получился одноразовый. И в принципе зачем было создавать итератор через класс, вот отличны итератор: for char in 'string': print(char.upper())
@user-de3op9hs4p
@user-de3op9hs4p 5 ай бұрын
Нет, не одноразовый. Каждый объект этого класса - одноразовый, да. Но так можно сказать и про ваш итератор for. Он одноразовый для 'string'. И Вы удивитесь, но Ваш for в общем-то, реализован точно так же, как вышеупомянутый класс, только под капотом. А зачем было писать собственный итератор? Да в учебных целях, чтобы понимать как он работает, и в частности, чтобы понять, как работает for.
@PsdmasterRu
@PsdmasterRu 2 жыл бұрын
Офигенное объяснение. Большое спасибо за видео!
Как устроены итераторы в Python?
11:13
Хитрый питон
Рет қаралды 15 М.
Best KFC Homemade For My Son #cooking #shorts
00:58
BANKII
Рет қаралды 61 МЛН
Little girl's dream of a giant teddy bear is about to come true #shorts
00:32
Clowns abuse children#Short #Officer Rabbit #angel
00:51
兔子警官
Рет қаралды 76 МЛН
Как устроены декораторы в python?
12:18
Хитрый питон
Рет қаралды 14 М.
Что такое __name__  в Python?
6:59
Хитрый питон
Рет қаралды 11 М.
Пример задачи на работу с динамической памятью в С++
9:54
Оксана Еськова. Основы программирования
Рет қаралды 321
Mutable и Immutable типы данных в python
8:31
Хитрый питон
Рет қаралды 15 М.
Как использовать *args и **kwargs в python?
11:22
Хитрый питон
Рет қаралды 6 М.
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
30:52
Best KFC Homemade For My Son #cooking #shorts
00:58
BANKII
Рет қаралды 61 МЛН