Для чего вы можете использовать функции генератора Python?
213
Я начинаю изучать Python, и я наткнулся на функции-генераторы, в которых есть оператор yield. Я хочу знать, какие проблемы эти функции действительно хорошо решают.
Генераторы дают вам ленивую оценку. Вы используете их, перебирая их, либо явно с помощью for, либо неявно, передавая их любой функции или конструкции, которая выполняет итерацию. Вы можете думать о генераторах как о возвращении нескольких элементов, как будто они возвращают список, но вместо того, чтобы возвращать их все сразу, они возвращают их один за другим, и функция генератора приостанавливается до тех пор, пока не будет запрошен следующий элемент.
Генераторы хороши для расчета больших наборов результатов (в частности, для расчета с использованием самих циклов), когда вы не знаете, нужны ли вам все результаты, или где вы не хотите выделять память для всех результатов одновременно , Или для ситуаций, когда генератор использует другой генератор или использует какой-то другой ресурс, и это более удобно, если это произошло как можно позже.
Другое использование для генераторов (это действительно то же самое) - замена обратных вызовов итерацией. В некоторых ситуациях вы хотите, чтобы функция выполняла большую работу и иногда сообщала об этом вызывающей стороне. Традиционно вы использовали бы функцию обратного вызова для этого. Вы передаете этот обратный вызов рабочей функции, и он будет периодически вызывать этот обратный вызов. Генераторный подход заключается в том, что рабочая функция (теперь генератор) ничего не знает о обратном вызове и просто возвращает всякий раз, когда она хочет сообщить что-то. Вызывающий объект вместо того, чтобы писать отдельный обратный вызов и передавать его в рабочую функцию, выполняет всю работу по отчетности в небольшом цикле for вокруг генератора.
Например, скажем, вы написали программу «поиск файловой системы». Вы можете выполнить поиск полностью, собрать результаты и затем отобразить их по одному. Все результаты должны быть собраны до того, как вы отобразите первый, и все результаты будут в памяти одновременно. Или вы можете отобразить результаты, пока вы их находите, что будет более эффективным с точки зрения памяти и намного более удобным для пользователя. Последнее можно сделать, передав функцию печати результатов в функцию поиска файловой системы, или это можно сделать, просто сделав функцию поиска генератором и итерировав результат.
Если вы хотите увидеть пример двух последних подходов, смотрите os.path.walk () (старая функция обхода файловой системы с обратным вызовом) и os.walk () (новый генератор обхода файловой системы.) Конечно, если Вы действительно хотели собрать все результаты в список, подход с генератором тривиален для преобразования в подход с большим списком:
Производит ли генератор, такой как тот, который создает списки файловой системы, действия параллельно с кодом, который запускает этот генератор в цикле? В идеале компьютер должен выполнять тело цикла (обрабатывая последний результат), одновременно делая то, что должен сделать генератор, чтобы получить следующее значение.
Стивен Лу
@StevenLu: Если только не нужно вручную запускать потоки до yieldи joinпосле, чтобы получить следующий результат, он не будет выполняться параллельно (и никакой стандартный генератор библиотек не делает этого; тайный запуск потоков не одобряется). Генератор делает паузу на каждом, yieldпока не будет запрошено следующее значение. Если генератор переносит операции ввода-вывода, ОС может активно кэшировать данные из файла, если предположить, что они будут запрошены в ближайшее время, но это ОС, Python не задействован.
ShadowRanger
90
Одна из причин использования генератора - сделать решение более понятным для некоторых решений.
Другой - обрабатывать результаты по одному, избегая создания огромных списков результатов, которые вы все равно будете обрабатывать отдельно.
Если у вас есть функция Фибоначчи до n, как эта:
# function versiondef fibon(n):
a = b =1
result =[]for i in xrange(n):
result.append(a)
a, b = b, a + breturn result
Вы можете легче написать функцию как это:
# generator versiondef fibon(n):
a = b =1for i in xrange(n):yield a
a, b = b, a + b
Функция понятнее. И если вы используете функцию, как это:
for x in fibon(1000000):print x,
в этом примере, если используется версия генератора, весь список 1000000 элементов не будет создан вообще, только одно значение за раз. Это не будет иметь место при использовании версии списка, где список будет создан первым.
Неочевидное использование генераторов - это создание прерываемых функций, которые позволяют выполнять такие вещи, как обновление пользовательского интерфейса или запускать несколько заданий «одновременно» (фактически с чередованием), не используя потоки.
Раздел «Мотивация» хорош тем, что в нем есть конкретный пример: «Когда функция производителя выполняет достаточно сложную работу, требующую поддержания состояния между созданными значениями, большинство языков программирования не предлагают приятного и эффективного решения, кроме добавления функции обратного вызова к аргументу производителя. список ... Например, tokenize.py в стандартной библиотеке использует такой подход »
Бен Криси
38
Я нахожу это объяснение, которое очищает мои сомнения. Потому что есть вероятность, что человек, который не знает, Generatorsтакже не знает оyield
Возвращение
В операторе return все локальные переменные уничтожаются, а полученное значение возвращается (возвращается) вызывающей стороне. Если через некоторое время эта же функция будет вызвана, она получит новый набор переменных.
Уступать
Но что, если локальные переменные не выбрасываются при выходе из функции? Это означает, что мы можем, resume the functionгде мы остановились. Это где понятие generatorsвводится и yieldзаявление возобновляется там, где functionостановился.
def generate_integers(N):for i in xrange(N):yield i
In[1]: gen = generate_integers(3)In[2]: gen<generator object at 0x8117f90>In[3]: gen.next()0In[4]: gen.next()1In[5]: gen.next()
Так вот в чем разница между returnи yieldутверждениями в Python.
Оператор Yield - это то, что делает функцию функцией-генератором.
Таким образом, генераторы - это простой и мощный инструмент для создания итераторов. Они написаны как обычные функции, но они используют yieldоператор всякий раз, когда хотят вернуть данные. Каждый раз, когда вызывается next (), генератор возобновляет работу с того места, где он остановился (он запоминает все значения данных и какой оператор был выполнен в последний раз).
Предположим, у вас есть 100 миллионов доменов в вашей таблице MySQL, и вы хотели бы обновить Alexa рейтинг для каждого домена.
Первое, что вам нужно, это выбрать ваши доменные имена из базы данных.
Допустим, ваша таблица называется, domainsа имя столбца domain.
Если вы используете, SELECT domain FROM domainsон вернет 100 миллионов строк, которые будут занимать много памяти. Таким образом, ваш сервер может произойти сбой.
Итак, вы решили запустить программу в пакетном режиме. Допустим, размер нашей партии составляет 1000.
В нашем первом пакете мы будем запрашивать первые 1000 строк, проверять Alexa рейтинг для каждого домена и обновлять строку базы данных.
В нашей второй партии мы будем работать над следующими 1000 рядами. В нашей третьей партии это будет с 2001 по 3000 и так далее.
Теперь нам нужна функция генератора, которая генерирует наши пакеты.
Вот наша функция генератора:
defResultGenerator(cursor, batchsize=1000):whileTrue:
results = cursor.fetchmany(batchsize)ifnot results:breakfor result in results:yield result
Как видите, наша функция сохраняет yieldрезультаты. Если вы используете ключевое слово returnвместо yield, тогда вся функция будет завершена, как только она достигнет возврата.
return- returns only onceyield- returns multiple times
Если функция использует ключевое слово, yieldто это генератор.
Теперь вы можете выполнять итерации следующим образом:
db =MySQLdb.connect(host="localhost", user="root", passwd="root", db="domains")
cursor = db.cursor()
cursor.execute("SELECT domain FROM domains")for result inResultGenerator(cursor):
doSomethingWith(result)
db.close()
было бы более практичным, если бы доход можно было объяснить с точки зрения рекурсивного / динамического программирования!
igaurav
27
Буферизация. Когда эффективно извлекать данные большими порциями, но обрабатывать их небольшими порциями, тогда может помочь генератор:
def bufferedFetch():whileTrue:
buffer = getBigChunkOfData()# insert some code to break on 'end of data'for i in buffer:yield i
Вышеизложенное позволяет легко отделить буферизацию от обработки. Функция потребителя теперь может просто получать значения по одному, не беспокоясь о буферизации.
Если getBigChuckOfData не ленив, то я не понимаю, какую выгоду дает здесь прибыль. Какой вариант использования этой функции?
Шон Джеффри Питц
1
Но дело в том, что IIUC, bufferedFetch лениво вызывает вызов getBigChunkOfData. Если getBigChunkOfData уже ленив, то bufferedFetch будет бесполезным. Каждый вызов bufferedFetch () будет возвращать один элемент буфера, даже если BigChunk уже был прочитан. И вам не нужно явно хранить счетчик следующего элемента для возврата, потому что механика yield делает это неявно.
hmijail оплакивает отставку
21
Я обнаружил, что генераторы очень помогают в очистке вашего кода и предоставляют вам уникальный способ инкапсуляции и модульности кода. В ситуации, когда вам нужно что-то, чтобы постоянно выдавать значения, основанные на его собственной внутренней обработке, и когда это нужно вызывать из любого места в вашем коде (а не только в цикле или блоке, например), генераторы - это возможность использовать.
Абстрактным примером будет генератор чисел Фибоначчи, который не живет в цикле, и когда он вызывается из любого места, всегда будет возвращать следующее число в последовательности:
def fib():
first =0
second =1yield first
yield second
while1:
next = first + second
yield next
first = second
second = next
fibgen1 = fib()
fibgen2 = fib()
Теперь у вас есть два объекта генератора чисел Фибоначчи, которые вы можете вызывать из любого места в вашем коде, и они всегда будут возвращать все большие числа Фибоначчи в следующей последовательности:
Прекрасная особенность генераторов состоит в том, что они инкапсулируют состояние, не проходя через циклы создания объектов. Один из способов думать о них как о «функциях», которые помнят их внутреннее состояние.
Я получил пример Фибоначчи от Python Generators. Что это? и с небольшим воображением вы можете придумать множество других ситуаций, когда генераторы создают отличную альтернативу forциклам и другим традиционным итерационным конструкциям.
В большинстве случаев все элементы iterableне обязательно должны быть там с самого начала, но могут быть сгенерированы на лету, когда они требуются. Это может быть намного более эффективным в обоих
пространство (вам никогда не нужно хранить все элементы одновременно) и
время (итерация может закончиться до того, как понадобятся все элементы).
В других случаях вы даже не знаете все предметы заранее. Например:
for command in user_input():
do_stuff_with(command)
У вас нет возможности узнать все команды пользователя заранее, но вы можете использовать хороший цикл, подобный этому, если у вас есть генератор, передающий вам команды:
... и бесконечная последовательность может быть сгенерирована путем многократного циклического перемещения по небольшому списку с возвратом к началу после достижения конца. Я использую это для выбора цветов на графиках или для создания занятых пульсирующих или вращающихся элементов в тексте.
Мое любимое использование - операции «фильтра» и «сокращения».
Допустим, мы читаем файл и хотим только строки, начинающиеся с «##».
def filter2sharps( aSequence ):for l in aSequence:if l.startswith("##"):yield l
Затем мы можем использовать функцию генератора в правильном цикле
source= file(...)for line in filter2sharps( source.readlines()):print line
source.close()
Пример сокращения аналогичен. Допустим, у нас есть файл, в котором нам нужно найти блоки <Location>...</Location>строк. [Не теги HTML, а строки, которые выглядят как теги.]
def reduceLocation( aSequence ):
keep=False
block=Nonefor line in aSequence:if line.startswith("</Location"):
block.append( line )yield block
block=None
keep=Falseelif line.startsWith("<Location"):
block=[ line ]
keep=Trueelif keep:
block.append( line )else:passif block isnotNone:yield block # A partial block, icky
Опять же, мы можем использовать этот генератор в правильном цикле for.
source = file(...)for b in reduceLocation( source.readlines()):print b
source.close()
Идея состоит в том, что функция генератора позволяет нам фильтровать или сокращать последовательность, создавая другую последовательность по одному значению за раз.
fileobj.readlines()будет читать весь файл в список в памяти, побеждая цель использования генераторов. Поскольку файловые объекты уже итерируемы, вы можете использовать for b in your_generator(fileobject):вместо них. Таким образом, ваш файл будет читаться по одной строке за раз, чтобы избежать чтения всего файла.
nosklo
ReduceLocation - довольно странный вывод списка, почему бы просто не выдать каждую строку? Кроме того, фильтр и уменьшение - это встроенные функции с ожидаемым поведением (см. Справку по ipython и т. Д.), Вы используете «уменьшить» так же, как и фильтр.
Джеймс Антилл
Хороший вопрос на readlines (). Я обычно понимаю, что файлы являются первоклассными линейными итераторами во время модульного тестирования.
С.Лотт
На самом деле, «сокращение» объединяет несколько отдельных строк в составной объект. Хорошо, это список, но это все еще сокращение от источника.
С.Лотт
9
Практический пример, где вы можете использовать генератор, если у вас есть какая-то форма и вы хотите перебирать ее углы, ребра или что-то еще. Для моего собственного проекта (исходный код здесь ) у меня был прямоугольник:
Теперь я могу создать прямоугольник и обвести его углы:
myrect=Rect(50,50,100,100)for corner in myrect:print(corner)
Вместо этого __iter__вы можете иметь метод iter_cornersи вызывать его с помощью for corner in myrect.iter_corners(). Это более элегантно использовать, __iter__поскольку мы можем использовать имя экземпляра класса непосредственно в forвыражении.
Однако, некоторые хорошие ответы здесь, я также рекомендую полностью прочитать учебник по функциональному программированию на Python, который поможет объяснить некоторые из наиболее эффективных сценариев использования генераторов.
Так как метод отправки генератора не был упомянут, вот пример:
def test():for i in xrange(5):
val =yieldprint(val)
t = test()# Proceed to 'yield' statement
next(t)# Send value to yield
t.send(1)
t.send('2')
t.send([3])
Показывает возможность отправки значения работающему генератору. Более yieldподробный курс по генераторам в видео ниже (включая объяснение, генераторы для параллельной обработки, выход за пределы рекурсии и т. Д.)
Кучи вещей. Каждый раз, когда вы хотите сгенерировать последовательность элементов, но не хотите «материализовать» их все в список сразу. Например, у вас может быть простой генератор, который возвращает простые числа:
def primes():
primes_found = set()
primes_found.add(2)yield2for i in itertools.count(1):
candidate = i *2+1ifnot all(candidate % prime for prime in primes_found):
primes_found.add(candidate)yield candidate
Затем вы можете использовать это для генерации продуктов следующих простых чисел:
def prime_products():
primeiter = primes()
prev = primeiter.next()for prime in primeiter:yield prime * prev
prev = prime
Это довольно тривиальные примеры, но вы можете увидеть, как это может быть полезно для обработки больших (потенциально бесконечных!) Наборов данных без их предварительной генерации, что является лишь одним из наиболее очевидных применений.
если нет (кандидат% простое число для простого в primes_found) должно быть, если все (кандидат% простое для простого в primes_found)
rjmunro
Да, я имел в виду написать «если не любой (кандидат% простое == 0 для простого в primes_found). Хотя ваш немного аккуратнее. :)
Ник Джонсон
Я полагаю, вы забыли удалить «не» из если не всех (кандидат% простое число для простого в primes_found)
Thava
0
Также хорошо для печати простых чисел до n:
def genprime(n=10):for num in range(3, n+1):for factor in range(2, num):if num%factor ==0:breakelse:yield(num)for prime_num in genprime(100):print(prime_num)
Ответы:
Генераторы дают вам ленивую оценку. Вы используете их, перебирая их, либо явно с помощью for, либо неявно, передавая их любой функции или конструкции, которая выполняет итерацию. Вы можете думать о генераторах как о возвращении нескольких элементов, как будто они возвращают список, но вместо того, чтобы возвращать их все сразу, они возвращают их один за другим, и функция генератора приостанавливается до тех пор, пока не будет запрошен следующий элемент.
Генераторы хороши для расчета больших наборов результатов (в частности, для расчета с использованием самих циклов), когда вы не знаете, нужны ли вам все результаты, или где вы не хотите выделять память для всех результатов одновременно , Или для ситуаций, когда генератор использует другой генератор или использует какой-то другой ресурс, и это более удобно, если это произошло как можно позже.
Другое использование для генераторов (это действительно то же самое) - замена обратных вызовов итерацией. В некоторых ситуациях вы хотите, чтобы функция выполняла большую работу и иногда сообщала об этом вызывающей стороне. Традиционно вы использовали бы функцию обратного вызова для этого. Вы передаете этот обратный вызов рабочей функции, и он будет периодически вызывать этот обратный вызов. Генераторный подход заключается в том, что рабочая функция (теперь генератор) ничего не знает о обратном вызове и просто возвращает всякий раз, когда она хочет сообщить что-то. Вызывающий объект вместо того, чтобы писать отдельный обратный вызов и передавать его в рабочую функцию, выполняет всю работу по отчетности в небольшом цикле for вокруг генератора.
Например, скажем, вы написали программу «поиск файловой системы». Вы можете выполнить поиск полностью, собрать результаты и затем отобразить их по одному. Все результаты должны быть собраны до того, как вы отобразите первый, и все результаты будут в памяти одновременно. Или вы можете отобразить результаты, пока вы их находите, что будет более эффективным с точки зрения памяти и намного более удобным для пользователя. Последнее можно сделать, передав функцию печати результатов в функцию поиска файловой системы, или это можно сделать, просто сделав функцию поиска генератором и итерировав результат.
Если вы хотите увидеть пример двух последних подходов, смотрите os.path.walk () (старая функция обхода файловой системы с обратным вызовом) и os.walk () (новый генератор обхода файловой системы.) Конечно, если Вы действительно хотели собрать все результаты в список, подход с генератором тривиален для преобразования в подход с большим списком:
источник
yield
иjoin
после, чтобы получить следующий результат, он не будет выполняться параллельно (и никакой стандартный генератор библиотек не делает этого; тайный запуск потоков не одобряется). Генератор делает паузу на каждом,yield
пока не будет запрошено следующее значение. Если генератор переносит операции ввода-вывода, ОС может активно кэшировать данные из файла, если предположить, что они будут запрошены в ближайшее время, но это ОС, Python не задействован.Одна из причин использования генератора - сделать решение более понятным для некоторых решений.
Другой - обрабатывать результаты по одному, избегая создания огромных списков результатов, которые вы все равно будете обрабатывать отдельно.
Если у вас есть функция Фибоначчи до n, как эта:
Вы можете легче написать функцию как это:
Функция понятнее. И если вы используете функцию, как это:
в этом примере, если используется версия генератора, весь список 1000000 элементов не будет создан вообще, только одно значение за раз. Это не будет иметь место при использовании версии списка, где список будет создан первым.
источник
list(fibon(5))
См. Раздел «Мотивация» в PEP 255 .
Неочевидное использование генераторов - это создание прерываемых функций, которые позволяют выполнять такие вещи, как обновление пользовательского интерфейса или запускать несколько заданий «одновременно» (фактически с чередованием), не используя потоки.
источник
Я нахожу это объяснение, которое очищает мои сомнения. Потому что есть вероятность, что человек, который не знает,
Generators
также не знает оyield
Возвращение
В операторе return все локальные переменные уничтожаются, а полученное значение возвращается (возвращается) вызывающей стороне. Если через некоторое время эта же функция будет вызвана, она получит новый набор переменных.
Уступать
Но что, если локальные переменные не выбрасываются при выходе из функции? Это означает, что мы можем,
resume the function
где мы остановились. Это где понятиеgenerators
вводится иyield
заявление возобновляется там, гдеfunction
остановился.Так вот в чем разница между
return
иyield
утверждениями в Python.Оператор Yield - это то, что делает функцию функцией-генератором.
Таким образом, генераторы - это простой и мощный инструмент для создания итераторов. Они написаны как обычные функции, но они используют
yield
оператор всякий раз, когда хотят вернуть данные. Каждый раз, когда вызывается next (), генератор возобновляет работу с того места, где он остановился (он запоминает все значения данных и какой оператор был выполнен в последний раз).источник
Пример из реального мира
Предположим, у вас есть 100 миллионов доменов в вашей таблице MySQL, и вы хотели бы обновить Alexa рейтинг для каждого домена.
Первое, что вам нужно, это выбрать ваши доменные имена из базы данных.
Допустим, ваша таблица называется,
domains
а имя столбцаdomain
.Если вы используете,
SELECT domain FROM domains
он вернет 100 миллионов строк, которые будут занимать много памяти. Таким образом, ваш сервер может произойти сбой.Итак, вы решили запустить программу в пакетном режиме. Допустим, размер нашей партии составляет 1000.
В нашем первом пакете мы будем запрашивать первые 1000 строк, проверять Alexa рейтинг для каждого домена и обновлять строку базы данных.
В нашей второй партии мы будем работать над следующими 1000 рядами. В нашей третьей партии это будет с 2001 по 3000 и так далее.
Теперь нам нужна функция генератора, которая генерирует наши пакеты.
Вот наша функция генератора:
Как видите, наша функция сохраняет
yield
результаты. Если вы используете ключевое словоreturn
вместоyield
, тогда вся функция будет завершена, как только она достигнет возврата.Если функция использует ключевое слово,
yield
то это генератор.Теперь вы можете выполнять итерации следующим образом:
источник
Буферизация. Когда эффективно извлекать данные большими порциями, но обрабатывать их небольшими порциями, тогда может помочь генератор:
Вышеизложенное позволяет легко отделить буферизацию от обработки. Функция потребителя теперь может просто получать значения по одному, не беспокоясь о буферизации.
источник
Я обнаружил, что генераторы очень помогают в очистке вашего кода и предоставляют вам уникальный способ инкапсуляции и модульности кода. В ситуации, когда вам нужно что-то, чтобы постоянно выдавать значения, основанные на его собственной внутренней обработке, и когда это нужно вызывать из любого места в вашем коде (а не только в цикле или блоке, например), генераторы - это возможность использовать.
Абстрактным примером будет генератор чисел Фибоначчи, который не живет в цикле, и когда он вызывается из любого места, всегда будет возвращать следующее число в последовательности:
Теперь у вас есть два объекта генератора чисел Фибоначчи, которые вы можете вызывать из любого места в вашем коде, и они всегда будут возвращать все большие числа Фибоначчи в следующей последовательности:
Прекрасная особенность генераторов состоит в том, что они инкапсулируют состояние, не проходя через циклы создания объектов. Один из способов думать о них как о «функциях», которые помнят их внутреннее состояние.
Я получил пример Фибоначчи от Python Generators. Что это? и с небольшим воображением вы можете придумать множество других ситуаций, когда генераторы создают отличную альтернативу
for
циклам и другим традиционным итерационным конструкциям.источник
Простое объяснение: рассмотрим
for
утверждениеВ большинстве случаев все элементы
iterable
не обязательно должны быть там с самого начала, но могут быть сгенерированы на лету, когда они требуются. Это может быть намного более эффективным в обоихВ других случаях вы даже не знаете все предметы заранее. Например:
У вас нет возможности узнать все команды пользователя заранее, но вы можете использовать хороший цикл, подобный этому, если у вас есть генератор, передающий вам команды:
С помощью генераторов вы также можете выполнять итерации по бесконечным последовательностям, что, конечно, невозможно при итерации по контейнерам.
источник
itertool
для этого - видитеcycles
.Мое любимое использование - операции «фильтра» и «сокращения».
Допустим, мы читаем файл и хотим только строки, начинающиеся с «##».
Затем мы можем использовать функцию генератора в правильном цикле
Пример сокращения аналогичен. Допустим, у нас есть файл, в котором нам нужно найти блоки
<Location>...</Location>
строк. [Не теги HTML, а строки, которые выглядят как теги.]Опять же, мы можем использовать этот генератор в правильном цикле for.
Идея состоит в том, что функция генератора позволяет нам фильтровать или сокращать последовательность, создавая другую последовательность по одному значению за раз.
источник
fileobj.readlines()
будет читать весь файл в список в памяти, побеждая цель использования генераторов. Поскольку файловые объекты уже итерируемы, вы можете использоватьfor b in your_generator(fileobject):
вместо них. Таким образом, ваш файл будет читаться по одной строке за раз, чтобы избежать чтения всего файла.Практический пример, где вы можете использовать генератор, если у вас есть какая-то форма и вы хотите перебирать ее углы, ребра или что-то еще. Для моего собственного проекта (исходный код здесь ) у меня был прямоугольник:
Теперь я могу создать прямоугольник и обвести его углы:
Вместо этого
__iter__
вы можете иметь методiter_corners
и вызывать его с помощьюfor corner in myrect.iter_corners()
. Это более элегантно использовать,__iter__
поскольку мы можем использовать имя экземпляра класса непосредственно вfor
выражении.источник
В основном избегание функций обратного вызова при переборе состояния поддержки ввода.
Смотрите здесь и здесь для обзора того, что можно сделать с помощью генераторов.
источник
Однако, некоторые хорошие ответы здесь, я также рекомендую полностью прочитать учебник по функциональному программированию на Python, который поможет объяснить некоторые из наиболее эффективных сценариев использования генераторов.
источник
Так как метод отправки генератора не был упомянут, вот пример:
Показывает возможность отправки значения работающему генератору. Более
yield
подробный курс по генераторам в видео ниже (включая объяснение, генераторы для параллельной обработки, выход за пределы рекурсии и т. Д.)Дэвид Бизли о генераторах на PyCon 2014
источник
Я использую генераторы, когда наш веб-сервер действует как прокси:
источник
Кучи вещей. Каждый раз, когда вы хотите сгенерировать последовательность элементов, но не хотите «материализовать» их все в список сразу. Например, у вас может быть простой генератор, который возвращает простые числа:
Затем вы можете использовать это для генерации продуктов следующих простых чисел:
Это довольно тривиальные примеры, но вы можете увидеть, как это может быть полезно для обработки больших (потенциально бесконечных!) Наборов данных без их предварительной генерации, что является лишь одним из наиболее очевидных применений.
источник
Также хорошо для печати простых чисел до n:
источник