В данный момент я читаю поваренную книгу по Python и в настоящее время изучаю генераторы. Мне трудно прийти в себя.
Поскольку я знаком с Java, существует ли эквивалент Java? В книге говорилось о "производителе / потребителе", однако, когда я слышу это, я думаю о потоковой обработке.
Что такое генератор и зачем вы его используете? Очевидно, без цитирования каких-либо книг (если только вы не можете найти достойный, упрощенный ответ прямо из книги). Возможно, с примерами, если вы чувствуете щедрость!
Переведено автоматически
Ответ 1
Примечание: в этом посте предполагается синтаксис Python 3.x.†
Генератор - это просто функция, которая возвращает объект, для которого вы можете вызвать next, такой, что при каждом вызове он возвращает некоторое значение, пока не вызовет StopIteration исключение, сигнализирующее о том, что все значения были сгенерированы. Такой объект называется итератором.
Обычные функции возвращают единственное значение с помощью return, как и в Java. Однако в Python есть альтернатива, называемая yield . Использование yield в любом месте функции превращает ее в генератор. Обратите внимание на этот код:
>>> defmyGen(n): ... yield n ... yield n + 1 ... >>> g = myGen(6) >>> next(g) 6 >>> next(g) 7 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
Как вы можете видеть, myGen(n) это функция, которая выдает n и n + 1. Каждый вызов next выдает одно значение, пока не будут получены все значения. for циклы вызывают next в фоновом режиме, таким образом:
>>> for n in myGen(6): ... print(n) ... 6 7
Точно так же существуют выражения генератора, которые предоставляют средства для краткого описания определенных распространенных типов генераторов:
>>> g = (n for n inrange(3, 5)) >>> next(g) 3 >>> next(g) 4 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
Обратите внимание, что выражения генератора во многом похожи на понимание списков:
>>> lc = [n for n inrange(3, 5)] >>> lc [3, 4]
Обратите внимание, что объект-генератор генерируется один раз, но его код не выполняется весь сразу. Вызовы только для next фактического выполнения (части) кода. Выполнение кода в генераторе прекращается, как только достигается yield оператор, после чего он возвращает значение. Следующий вызов next затем приводит к продолжению выполнения в том состоянии, в котором генератор был оставлен после последнего yield. Это фундаментальное отличие от обычных функций: они всегда начинают выполнение с "вершины" и сбрасывают свое состояние при возврате значения.
По этому вопросу можно сказать еще кое-что. Например, можно send преобразовать данные обратно в генератор (ссылка). Но это то, что я предлагаю вам не рассматривать, пока вы не поймете основную концепцию генератора.
Теперь вы можете спросить: зачем использовать генераторы? Есть пара веских причин:
Некоторые концепции можно описать гораздо более кратко, используя генераторы.
Вместо создания функции, которая возвращает список значений, можно написать генератор, который генерирует значения "на лету". Это означает, что не нужно создавать список, а это означает, что результирующий код более экономно использует память. Таким образом можно даже описать потоки данных, которые просто были бы слишком большими, чтобы поместиться в памяти.
Генераторы позволяют естественным образом описывать бесконечные потоки. Рассмотрим, например, числа Фибоначчи:
>>> deffib(): ... a, b = 0, 1 ... whileTrue: ... yield a ... a, b = b, a + b ... >>> import itertools >>> list(itertools.islice(fib(), 10)) [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Этот код использует itertools.islice для извлечения конечного числа элементов из бесконечного потока. Мы советуем вам внимательно ознакомиться с функциями в itertools модуле, поскольку они являются важными инструментами для написания продвинутых генераторов с большой легкостью.
О Python <=2.6:† в приведенных выше примерах next есть функция, которая вызывает метод __next__ для данного объекта. В Python <=2.6 используется немного другая техника, а именно o.next() вместо next(o). В Python 2.7 есть next() call .next, поэтому вам не нужно использовать следующее в 2.7:
>>> g = (n for n inrange(3, 5)) >>> g.next() 3
Ответ 2
Генератор фактически представляет собой функцию, которая возвращает (данные) до завершения, но в этот момент он приостанавливается, и вы можете возобновить работу функции в этот момент.
>>> myGeneratorInstance = myGenerator() >>> next(myGeneratorInstance) These >>> next(myGeneratorInstance) words
и так далее. Преимущество генераторов (или одно из них) заключается в том, что, поскольку они обрабатывают данные по частям за раз, вы можете работать с большими объемами данных; со списками чрезмерные требования к памяти могут стать проблемой. Генераторы, как и списки, являются итеративными, поэтому их можно использовать теми же способами:
>>> for word in myGeneratorInstance: ... print word These words come one at a time
Обратите внимание, что генераторы предоставляют другой способ работы с бесконечностью, например
>>> from time import gmtime, strftime >>> defmyGen(): ... whileTrue: ... yield strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime()) >>> myGeneratorInstance = myGen() >>> next(myGeneratorInstance) Thu, 28 Jun 200114:17:15 +0000 >>> next(myGeneratorInstance) Thu, 28 Jun 200114:18:02 +0000
Генератор инкапсулирует бесконечный цикл, но это не проблема, потому что вы получаете каждый ответ только тогда, когда запрашиваете его.
Ответ 3
Прежде всего, термин генератор изначально был несколько нечетко определен в Python, что привело к большой путанице. Вы, вероятно, имеете в виду итераторы и итерируемые (см. Здесь). Тогда в Python есть также функции генератора (которые возвращают объект генератора), объекты генератора (которые являются итераторами) и выражения генератора (которые вычисляются для объекта генератора).
Согласно записи глоссария для генератора, похоже, что официальная терминология теперь такова, что generator - это сокращение от "функции генератора". В прошлом в документации термины определялись непоследовательно, но, к счастью, это было исправлено.
Все же было бы неплохо быть точным и избегать термина "генератор" без дальнейших уточнений.
Ответ 4
Генераторы можно рассматривать как сокращение для создания итератора. Они ведут себя как итератор Java. Пример:
>>> g = (x for x inrange(10)) >>> g <generator object <genexpr> at 0x7fac1c1e6aa0> >>> g.next() 0 >>> g.next() 1 >>> g.next() 2 >>> list(g) # force iterating the rest [3, 4, 5, 6, 7, 8, 9] >>> g.next() # iterator is at the end; calling next again will throw Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
Надеюсь, это поможет / это то, что вы ищете.
Обновить:
Как показывают многие другие ответы, существуют разные способы создания генератора. Вы можете использовать синтаксис скобок, как в моем примере выше, или вы можете использовать yield . Еще одна интересная особенность заключается в том, что генераторы могут быть "бесконечными" - итераторами, которые не останавливаются:
>>> definfinite_gen(): ... n = 0 ... whileTrue: ... yield n ... n = n + 1 ... >>> g = infinite_gen() >>> g.next() 0 >>> g.next() 1 >>> g.next() 2 >>> g.next() 3 ...