Где лучше использовать yield в Python? - PullRequest
28 голосов
/ 25 октября 2011

Я знаю, как работает yield. Я знаю перестановку, думаю, это просто как математическая простота.

Но какова истинная сила yield? Когда я должен использовать это? Простой и хороший пример лучше.

Ответы [ 4 ]

56 голосов
/ 25 октября 2011

yield лучше всего использовать, когда у вас есть функция, которая возвращает последовательность, и вы хотите выполнить итерацию по этой последовательности, но вам не нужно иметь каждое значение в памяти сразу.

Например,У меня есть сценарий Python, который анализирует большой список файлов CSV, и я хочу, чтобы каждая строка возвращалась для обработки в другой функции.Я не хочу хранить мегабайты данных в памяти сразу, поэтому я yield каждая строка в структуре данных Python.Таким образом, функция для получения строк из файла может выглядеть примерно так:

def get_lines(files):
    for f in files:
        for line in f:
            #preprocess line
            yield line

Затем я могу использовать тот же синтаксис, что и для списков, для доступа к выводу этой функции:

for line in get_lines(files):
    #process line

но я экономлю много памяти.

15 голосов
/ 25 октября 2011

Проще говоря, 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 для продвижения по стеку вызовов вместо использования некоторой переменной стека.Генераторы также могут быть использованы для специализированного обхода дерева и всего прочего.

Дополнительная литература:

3 голосов
/ 25 октября 2011

Другое использование в сетевом клиенте. Используйте «yield» в функции генератора для обхода нескольких сокетов без сложностей потоков.

Например, у меня был клиент для тестирования оборудования, который должен был посылать плоскости изображения R, G, B в прошивку. Данные необходимо отправлять в режиме реального времени: красный, зеленый, синий, красный, зеленый, синий. Вместо того, чтобы порождать три потока, у меня был генератор, который считывал из файла кодированный буфер. Каждый буфер представлял собой «buf yield». Конец файла, функция возвращена, и у меня был конец итерации.

Мой клиентский код прошел через три функции генератора, получая буферы до конца итерации.

1 голос
/ 29 августа 2013

Я читаю Структуры данных и алгоритмы в Python

Существует функция Фабоначчи, использующая yield.Я думаю, что это лучший момент для использования yield.

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a+b

Вы можете использовать это как:

f = fibonacci()
for i, f in enumerate(f):
    print i, f
    if i >= 100: break

Итак, я думаю, может быть, когда следующий элемент зависит от предыдущих элементовпришло время использовать yield.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...