Pull to refresh

Comments 45

В избранное, немедленно в избранное!
P.S. Большое спасибо за понятное объяснение.
Все это очень хорошо описывается в «Learning Python» Mark Lutz, всем кто не читал настоятельно рекомендую
Именно его сейчас читаю.
Спасибо, стало гораздо понятнее.
Тоже сохранил в избранное.
Кажется, уже было, но в избранном пусть полежит. А то я знаю про них, умею пользоваться, но не использую — в питоне с большим объемом данных не работаю.
Сорри за некропостинг. Это не только с большими данными. К примеру поиск файла с определенными критериями как Вы организовываете?
1.Очень многие сначала пробегаются по директории рекурсивно, создают список, а потом в for-конструкции берут очередной элемент этого списка, т.е. имя файла и уже работают с ним.
2. А это можно значительно улучшить тем, что как только нашли имя файла, то вместо сохранения в список, сразу же yield filename.
def node._get_child_candidates — разве это нормально? Скорее всего имелось в виду — def _get_child_candidates
И действительно, что отмечают и в комментариях на SO:
def anobject.method(): pass is invalid syntax in Python. – J.F. Sebastian Oct 24 '08 at 19:09

Посмотрел в исходники (http://well-adjusted.de/~jrschulz/mspace/mspace-pysrc.html#VPTree._get_child_candidates) — там тоже такого нет. Непонятно, в общем, откуда автор вопроса такое взял.
Пару месяцев назад разобрался с yield именно благодаря этому топику на stackoverflow.
Смутно вспоминаю, что в документации было написано, что не стоит менять список во время итерации по нему.
Это не совсем модификация списка, это такой хитрый способ перебрать дерево.
Разве? У нас же while по candidates.

А в теле цикла candidates.extend.
в данном примере нет итерации по списку. ;)
возможно, чтобы исключить путаницу, авторам нужно было воспользоваться collections.deque для реализации стека, а не list.
Был неправ. Там while candidates и candidates.pop, а не for… in candidates.
Посмотрел невнимательно. Типичный стек.

Как это нет, вроде как раз есть. Каждый раз candidates модифицируются ссылками на узлы, при вызове extend. То, что каждый раз выкусывается самый ранний делу не помогает. Или я что-то не понял.

Наверное, также стоит добавить, что итерирование завершается за счет выбрасывания исключения StopIteration. Т.е. вот этот код

for i in gen:
     print i

на самом деле работает как-то так:

try:
    while True:
        print gen.next()
catch StopIteration:
    pass


Это важно понимать, если для итерирования генеретора не достаточно констрункции for ... in, и нужно закрутить что-то с while'ом.
Только конечно не catch, а except. )
Еще, в статье не упоминается, что в коде генератора также можно использовать return, но без возвращаемого значения, который воспринимается, как завершение итерирования и аналогичен raise StopIteration
UFO landed and left these words here
А насколько в питоне исключения быстро работают? В некоторых языках, например жава, не рекомендуется использовать исключения для штатных ситуаций, потому что это работает не очень быстро. В питоне, получается, такого нет.
Питон не позиционируется как системный язык, а потому в вопросе использования исключений принято исходить из удобства. Ситуация, когда функция возвращает «не успешность» своего выполнения как значение (return) является скорее антипаттерном.
Например, через yield генератор может не только возвращать значение, но и принимать произвольный аргумент:

Более того, это само интересное в yield-е и есть. yield i разжёвывать и смысла особого нет — ну генераторы и генераторы. А вот j = yield i позволяет делать сопроцедуры (coroutines). Которые открывают совершенно новые возможности в Python-е, от цепочек «потребителей» (в смысле, они являются как бы антонимом к «генераторам», но, точно так же, как и генераторы, могут объединяться в цепочки) до кооперативной многозадачности (!!!).

На эту тему есть хорошая (правда, длинная и на английском) презентация: www.dabeaz.com/coroutines/Coroutines.pdf
UFO landed and left these words here
«А никто не обещал, что будет легко» :)

На самом деле, метафора generator vs consumer очень хорошо подходит для первого сценария использования.
Ну, а кооперативная многозадачность… впрочем, вряд ли кому понадобится заниматься ей на таком низком уровне (а не хотя бы на уровне eventlet/gevent).
На основе этого сделан inline callback в асинхронных фреймворках twisted, web tornado.
Шикарная вещь, узнал об этом буквально неделю назад. Понял, что во многих местах нужно было использовать именно g.send() вместо костылей.
Итерация это процесс, включающий итерируемые объекты (реализующие метод __iter__()) и итераторы (реализующие __next__())

Только не __next_()_, а next().
Отличное изложение, все понятно и по делу.
>Генераторам же предшествуют итераторы.

Всё никак не могу понять: в теории итераторы и генераторы — это просто множества со схожими свойствами/интерфейсами или одно из них является подмножеством (или наследником, если угодно) другого? Является ли корректным высказывание: «Любой итератор — это генератор»?
Бывают iterators, iterables и generators.
Iterable (iterable interface) — интерфейс, позволяющий итерироваться (метод next/__next__ и raise StopIteration в конце).
Iterator — объект с iterable interface.
Generator — функция, возвращающая iterator.

The Python Tutorial — Classes
Building Skills in Python — Iterators and Generators
Да StackOverflow уже не торт, там всякие Димы Маликовы тусуются.
UFO landed and left these words here
Ужасно!
В объяснении удалось избежать функциональных замыканий, continuations и сопроцедур!
а в питоне поддерживается возбуждение исключения в месте yield?
UFO landed and left these words here
Требую мануал по рекурсивным генераторам! :)

Может случиться так, что потомков много и хранить их всех в памяти не хочется.

Не понял. А вот этот код тогда что делает?

candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

Допустим в candidates лежат два элемента. Если _get_child_candidates возвращает генератор, который при вызове вернет еще два (т.е. и правый и левый потомки есть), то candidates увеличится на два элемента? Или же у него будет состояние "первые два элемента в нулевой и первой ячейках, а если попросят, что в третьей, то нужно будет вызвать генератор"?

Или же здесь candidates.extend сразу вычитывает весь генератор, который вернул метод _get_child_candidates, и добавляет все элементы из генератора в конец candidates сразу?

На мой вгляд здесь действительно есть место введению в заблуждение. extend как раз-таки добавит ссыли на узлы. Генератор тут вообще ничего не оптимизирует. То же самое можно было сделать с обычными ссылками. Либо я что-то тоже не понимаю.

Когда вы создаёте список, используя генераторное выражение, вы создаёте также итератор:

Я бы был аккуратнее, потому что есть ещё вот такая конструкция (круглые скобки):

myList = (x * x for x in range(3))

И вот это как раз я бы назвал генераторным выражением. Как минимум, я бы добавлял в скобках оригинальное название терминов, которые трудно переводятся (в данном случае это list comprehension).

Всё, к чему можно применить конструкцию «for… in...», является итерируемым объектом:

А зачем вы переводите так, что каждый раз у вас новый термин ? В оригинале заголовок Iterables - вы его перевели как Итераторы . Тут то же слово в оригинале, но теперь он у вас итерируемый объект .

Ваш код будет вызываться каждый раз, когда for обращается к генератору.

Вводит в заблуждение. Оригинал: "Then, your code will continue from where it left off each time for uses the generator." - Каждый раз, когда for обращается к генератору на очередной итерации, ваша функция продолжит с того места, где она была прервана на предыдущей итерации. Как-то так.

Этот код содержит несколько меньших частей:

Там "smart parts", а не "small parts".

Цикл итерируется по списку, но списко расширяется во время итерации :-) Это лаконичный способ обойти все сгрупиррованные данные, зоть это и немного опасно, так как может обернуться бесконечным циклом. В таком случае

Тут вообще с точностью до наоборот. Не в таком случае, а в этом (имеется в виду случай, описанный в коде). А у вас тут как будто вы дальше продолжаете обосновывать предыдущее утверждение про опасность бесконечного цикла. Хотя автор просто описывает дальше, как код работает.

который ожидает на вход что-нибудь итерируемое и добавляет его значения к списку.

Теперь iterable превратилось в что-нибудь итерируемое.

Дальше уже не стал корректировать.

Перевод на троечку. Лучше прочтите оригинал.

Sign up to leave a comment.

Articles