Проще говоря, yield
дает вам генератор.Вы бы использовали его там, где обычно использовали бы return
в функции.Как действительно надуманный пример, вырезанный и вставленный из приглашения ...
>>> def get_odd_numbers(i):
... return range(1, i, 2)
...
>>> def yield_odd_numbers(i):
... for x in range(1, i, 2):
... yield x
...
>>> foo = get_odd_numbers(10)
>>> bar = yield_odd_numbers(10)
>>> foo
[1, 3, 5, 7, 9]
>>> bar
<generator object yield_odd_numbers at 0x1029c6f50>
>>> bar.next()
1
>>> bar.next()
3
>>> bar.next()
5
Как видите, в первом случае foo
содержит весь список в памяти сразу.Это не имеет большого значения для списка из 5 элементов, но что, если вы хотите список из 5 миллионов?Мало того, что это огромный пожиратель памяти, он также требует много времени для создания во время вызова функции.Во втором случае bar
просто дает вам генератор.Генератор является итеративным - это означает, что вы можете использовать его в цикле for и т. Д., Но к каждому значению можно получить доступ только один раз.Все значения также не сохраняются в памяти одновременно;объект генератора «запоминает», где он находился в цикле в последний раз, когда вы его вызывали - таким образом, если вы используете итеративный подсчет (скажем) до 50 миллиардов, вам не нужно считать до 50 миллиардов всехи запомните 50 миллиардов чиселОпять же, это довольно надуманный пример, вы, вероятно, использовали бы itertools
, если бы вы действительно хотели сосчитать до 50 миллиардов.:)
Это самый простой вариант использования генераторов.Как вы сказали, его можно использовать для написания эффективных перестановок, используя yield
для продвижения по стеку вызовов вместо использования некоторой переменной стека.Генераторы также могут быть использованы для специализированного обхода дерева и всего прочего.
Дополнительная литература: