Что делает ключевое слово "yield"?

Каково использование ключевого слова yield в 9X_listiterator Python? Что он делает?

Например, я пытаюсь 9X_python-shell понять этот код 1 :

def _get_child_candidates(self, distance, min_dist, max_dist):
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild  

А это звонящий:

result, candidates = [], [self]
while candidates:
    node = candidates.pop()
    distance = node._get_dist(obj)
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result

Что происходит 9X_listiterator при вызове метода _get_child_candidates? Список возвращается? Единый 9X_python элемент? Это снова называется? Когда прекратятся 9X_py последующие вызовы?


1. Этот фрагмент кода написал Йохен Шульц (jrschulz), который создал отличную библиотеку Python для метрических пространств. Это ссылка на полный источник: [Модуль mspace] [1].

12431
1

  • Смешное количество голосов з ...
49
Общее количество ответов: 49

Ответ #1

Ответ на вопрос: Что делает ключевое слово "yield"?

Чтобы понять, что делает yield, вы должны понимать, что 9X_yield-keyword такое генераторы. И прежде чем вы сможете понять генераторы, вы 9X_python должны понять итерации.

Итерационные объекты

Создавая список, вы можете 9X_listiterator читать его элементы один за другим. Чтение 9X_py его элементов по очереди называется итерацией:

>>> mylist = [1, 2, 3]
>>> for i in mylist:
...    print(i)
1
2
3

mylist - итерируемый. Когда 9X_iterator вы используете понимание списка, вы создаете 9X_python список и, следовательно, итерируемый:

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4

Все, что 9X_generators вы можете использовать для «for... in...», является 9X_listiterator итеративным; lists, strings, файлы ...

Эти итерации удобны, потому 9X_python что вы можете читать их сколько угодно, но 9X_generators вы храните все значения в памяти, и это 9X_coroutine не всегда то, что вам нужно, когда у вас 9X_iterator много значений.

Генераторы

Генераторы - это итераторы, своего 9X_py рода итерации, которые можно выполнять только один раз. Генераторы не хранят все 9X_iterators значения в памяти, они генерируют значения на лету:

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
0
1
4

Это то же самое, за 9X_python-interpreter исключением того, что вы использовали () вместо 9X_yield []. НО, вы не можете выполнить for i in mygenerator второй раз, поскольку 9X_generator генераторы можно использовать только один 9X_python-shell раз: они вычисляют 0, затем забывают об 9X_iterator этом и вычисляют 1 и заканчивают вычисление 9X_py 4, один за другим.

Доходность

yield - ключевое слово, которое 9X_coroutine используется как return, за исключением того, что 9X_yield функция возвращает генератор.

>>> def create_generator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = create_generator() # create a generator
>>> print(mygenerator) # mygenerator is an object!

>>> for i in mygenerator:
...     print(i)
0
1
4

Это бесполезный 9X_python-interpreter пример, но он удобен, когда вы знаете, что 9X_python-shell ваша функция вернет огромный набор значений, которые 9X_listiterator вам нужно будет прочитать только один раз.

Чтобы 9X_listiterator освоить yield, вы должны понимать, что при вызове функции код, который вы написали в теле функции, не запускается. Функция 9X_yield-keyword возвращает только объект генератора, это 9X_generators немного сложно.

Затем ваш код будет продолжаться 9X_pythonic с того места, где он остановился, каждый 9X_yield раз, когда for использует генератор.

Теперь 9X_yield-keyword самое сложное:

Когда for в первый раз вызывает 9X_generators объект-генератор, созданный из вашей функции, он 9X_coroutine запускает код в вашей функции с самого начала, пока 9X_yield-keyword не достигнет yield, а затем вернет первое значение 9X_python-interpreter цикла. Затем каждый последующий вызов будет 9X_generator запускать другую итерацию цикла, который 9X_python-interpreter вы написали в функции, и возвращать следующее 9X_python-interpreter значение. Это будет продолжаться до тех 9X_python-shell пор, пока генератор не будет считаться пустым, что 9X_coroutine происходит, когда функция выполняется без 9X_python-shell нажатия yield. Это может быть связано с тем, что 9X_coroutine цикл подошел к концу или вы больше не удовлетворяете 9X_listiterator "if/else".


Объяснение вашего кода

Генератор:

# Here you create the method of the node object that will return the generator
def _get_child_candidates(self, distance, min_dist, max_dist):

    # Here is the code that will be called each time you use the generator object:

    # If there is still a child of the node object on its left
    # AND if the distance is ok, return the next child
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild

    # If there is still a child of the node object on its right
    # AND if the distance is ok, return the next child
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild

    # If the function arrives here, the generator will be considered empty
    # there is no more than two values: the left and the right children

Абонент:

# Create an empty list and a list with the current object reference
result, candidates = list(), [self]

# Loop on candidates (they contain only one element at the beginning)
while candidates:

    # Get the last candidate and remove it from the list
    node = candidates.pop()

    # Get the distance between obj and the candidate
    distance = node._get_dist(obj)

    # If distance is ok, then you can fill the result
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)

    # Add the children of the candidate in the candidate's list
    # so the loop will keep running until it will have looked
    # at all the children of the children of the children, etc. of the candidate
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

return result

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

  • Цикл 9X_iterator повторяется по списку, но список расширяется 9X_generator во время итерации цикла. Это краткий способ 9X_python-interpreter просмотреть все эти вложенные данные, даже 9X_generator если это немного опасно, поскольку вы можете 9X_pythonic получить бесконечный цикл. В этом случае 9X_generators candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) исчерпывает все значения генератора, но 9X_yield while продолжает создавать новые объекты генератора, которые 9X_yield будут давать значения, отличные от предыдущих, поскольку 9X_pythonista он не применяется к тому же узлу.

  • Метод extend() - это 9X_iterator метод объекта списка, который ожидает итерацию 9X_pythonic и добавляет свои значения в список.

Обычно 9X_generators мы передаем ему список:

>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]

Но в вашем коде есть 9X_coroutine генератор, и это хорошо, потому что:

  1. Вам не нужно читать значения дважды.
  2. У вас может быть много детей, и вы не хотите, чтобы все они хранились в памяти.

И это 9X_python работает, потому что Python не заботится, является 9X_python-shell ли аргумент метода списком или нет. Python 9X_python-shell ожидает итераций, поэтому он будет работать 9X_pythonista со строками, списками, кортежами и генераторами! Это 9X_py называется утиной типизацией и является 9X_pythonic одной из причин, по которой Python такой 9X_python крутой. Но это уже другая история, другой 9X_yield-keyword вопрос ...

Вы можете остановиться на этом 9X_coroutine или прочитать немного, чтобы увидеть расширенное 9X_yield-keyword использование генератора:

Контроль истощения генератора

>>> class Bank(): # Let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self):
...        while not self.crisis:
...            yield "$100"
>>> hsbc = Bank() # When everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # Crisis is coming, no more money!
>>> print(corner_street_atm.next())

>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs
>>> print(wall_street_atm.next())

>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())

>>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business
>>> for cash in brand_new_atm:
...    print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...

Примечание. Для Python 3 используйте 9X_coroutine print(corner_street_atm.__next__()) или print(next(corner_street_atm))

.

Это может быть полезно для различных 9X_py вещей, например для управления доступом 9X_py к ресурсу.

Itertools, ваш лучший друг

Модуль itertools содержит специальные 9X_py функции для управления итерациями. Вы когда-нибудь 9X_iterator хотели продублировать генератор? Связать 9X_listiterator два генератора? Сгруппировать значения во 9X_pythonic вложенном списке с однострочником? Map / Zip без 9X_python-shell создания другого списка?

Тогда просто import itertools.

Пример? Посмотрим 9X_yield-keyword возможные порядки прибытия на скачки на 9X_python-interpreter четырех лошадях:

>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)

>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
 (1, 2, 4, 3),
 (1, 3, 2, 4),
 (1, 3, 4, 2),
 (1, 4, 2, 3),
 (1, 4, 3, 2),
 (2, 1, 3, 4),
 (2, 1, 4, 3),
 (2, 3, 1, 4),
 (2, 3, 4, 1),
 (2, 4, 1, 3),
 (2, 4, 3, 1),
 (3, 1, 2, 4),
 (3, 1, 4, 2),
 (3, 2, 1, 4),
 (3, 2, 4, 1),
 (3, 4, 1, 2),
 (3, 4, 2, 1),
 (4, 1, 2, 3),
 (4, 1, 3, 2),
 (4, 2, 1, 3),
 (4, 2, 3, 1),
 (4, 3, 1, 2),
 (4, 3, 2, 1)]

Понимание внутренних механизмов итерации

Итерация - это процесс, включающий 9X_generator итераторы (реализация метода __iter__()) и итераторы 9X_pythonic (реализация метода __next__()). Итерируемые объекты 9X_pythonic - это любые объекты, из которых вы можете 9X_yield-keyword получить итератор. Итераторы - это объекты, которые 9X_python-interpreter позволяют выполнять итерацию по итерациям.

Подробнее 9X_iterators об этом в статье о how for loops work.

17313
8

  • @MatthiasFripp «Это продолжается до тех пор, пока функция не завершится до конца» - или пока не встретится оператор `return`. (`return` разрешен в функции, содержащей` yie ...

Ответ #2

Ответ на вопрос: Что делает ключевое слово "yield"?

Быстрый путь к пониманию yield

Когда вы видите функцию с операторами yield, примените 9X_coroutine этот простой трюк, чтобы понять, что произойдет:

  1. Вставьте строку result = [] в начало функции.
  2. Замените каждый yield expr на result.append(expr).
  3. Вставьте строку return result внизу функции.
  4. Ура, больше никаких инструкций yield! Прочтите и разберитесь в коде.
  5. Сравните функцию с исходным определением.

Этот 9X_generator трюк может дать вам представление о логике 9X_yield-keyword функции, но то, что на самом деле происходит 9X_coroutine с yield, значительно отличается от того, что 9X_iterator происходит при подходе на основе списков. Во 9X_generators многих случаях подход yield будет намного 9X_py эффективнее и быстрее. В других случаях 9X_python этот трюк заставит вас застрять в бесконечном 9X_pythonic цикле, даже если исходная функция работает 9X_iterators нормально. Читайте дальше, чтобы узнать 9X_python больше ...

Не путайте свои итерации, итераторы и генераторы

Во-первых, протокол итератора - когда вы пишете

for x in mylist:
    ...loop body...

Python 9X_iterators выполняет следующие два шага:

  1. Получает итератор 9X_yield для mylist:

    Вызов iter(mylist) -> возвращает объект с помощью 9X_iterator метода next() (или __next__() в Python 3).

    [Это шаг, о котором 9X_yield-keyword большинство людей забывают вам рассказывать]

  2. Использует 9X_python-shell итератор для перебора элементов:

    Продолжайте 9X_generators вызывать метод next() на итераторе, возвращенном 9X_python-interpreter с шага 1. Возвращаемое значение из next() присваивается 9X_python x, и тело цикла выполняется. Если исключение 9X_coroutine StopIteration возникает из next(), это означает, что в итераторе 9X_iterators больше нет значений и цикл завершен.

На самом 9X_listiterator деле Python выполняет два вышеуказанных 9X_pythonic шага каждый раз, когда ему требуется перебрать содержимое 9X_python-shell объекта - так что это может быть цикл for, но 9X_iterator это также может быть код вроде otherlist.extend(mylist) ( где otherlist - список 9X_generator Python).

Здесь mylist - итерируемый, потому что он реализует 9X_coroutine протокол итератора. В определяемом пользователем 9X_python-interpreter классе вы можете реализовать метод __iter__(), чтобы 9X_iterators сделать экземпляры вашего класса итерируемыми. Этот 9X_python-shell метод должен возвращать итератор. Итератор - это 9X_python-shell объект с методом next(). Можно реализовать как 9X_python-shell __iter__(), так и next() в одном и том же классе, и чтобы 9X_listiterator __iter__() возвращал self. Это сработает для простых случаев, но 9X_iterator не тогда, когда вы хотите, чтобы два итератора 9X_pythonic проходили цикл по одному и тому же объекту 9X_python-shell одновременно.

Итак, протокол итератора, многие 9X_listiterator объекты реализуют этот протокол:

  1. Встроенные списки, словари, кортежи, наборы, файлы.
  2. Пользовательские классы, реализующие __iter__().
  3. Генераторы.

Обратите 9X_yield-keyword внимание, что цикл for не знает, с каким объектом 9X_python-interpreter он имеет дело - он просто следует протоколу 9X_python-shell итератора и с радостью получает элемент 9X_generators за элементом при вызове next(). Встроенные списки 9X_python-interpreter возвращают свои элементы один за другим, словари 9X_iterator возвращают ключи один за другим, файлы возвращают 9X_yield строки одну за другой и т. Д. И генераторы возвращают 9X_py ... ну вот где появляется yield:

def f123():
    yield 1
    yield 2
    yield 3

for item in f123():
    print item

Вместо операторов 9X_yield yield, если у вас есть три оператора return в f123(), будет 9X_listiterator выполнен только первый, и функция завершится. Но 9X_pythonista f123() - необычная функция. Когда вызывается f123(), он 9X_iterator не возвращает ни одно из значений в операторах 9X_iterators yield! Он возвращает объект-генератор. Кроме 9X_iterators того, функция действительно не выходит - она 9X_generators ​​переходит в приостановленное состояние. Когда 9X_pythonista цикл for пытается перебрать объект-генератор, функция 9X_generator возобновляет работу из приостановленного 9X_yield состояния на следующей строке после yield, из 9X_python-shell которого она ранее возвратилась, выполняет 9X_pythonic следующую строку кода, в данном случае yield и 9X_python-interpreter возвращает его как следующий элемент. Это 9X_python-shell происходит до тех пор, пока функция не завершится, после 9X_listiterator чего генератор поднимет StopIteration и цикл не завершится.

Таким 9X_iterators образом, объект-генератор похож на адаптер 9X_yield - с одной стороны, он демонстрирует протокол 9X_iterator итератора, предоставляя методы __iter__() и next(), чтобы 9X_coroutine цикл for оставался счастливым. На другом конце, однако, он 9X_pythonista запускает функцию ровно настолько, чтобы 9X_py получить из нее следующее значение, и возвращает 9X_iterator ее в приостановленный режим.

Зачем нужны генераторы?

Обычно вы можете 9X_python-interpreter написать код, который не использует генераторы, но 9X_iterators реализует ту же логику. Один из вариантов 9X_iterators - использовать «трюк» с временным списком, о 9X_iterators котором я упоминал ранее. Это не будет работать 9X_yield во всех случаях, например, если у вас бесконечные 9X_coroutine циклы, или он может неэффективно использовать 9X_pythonic память, когда у вас действительно длинный 9X_py список. Другой подход - реализовать новый 9X_python итеративный класс SomethingIter, который 9X_py сохраняет состояние в членах экземпляра 9X_py и выполняет следующий логический шаг в своем 9X_python-interpreter методе next() (или __next__() в Python 3). В зависимости 9X_python-shell от логики код внутри метода next() может выглядеть 9X_listiterator очень сложным и быть подверженным ошибкам. Здесь 9X_python генераторы представляют собой простое и 9X_listiterator понятное решение.

2413
3

  • * "Когда вы видите функцию с операторами yield, примените этот просто ...

Ответ #3

Ответ на вопрос: Что делает ключевое слово "yield"?

Подумайте об этом так:

Итератор - это просто 9X_python причудливый термин для объекта, у которого 9X_yield-keyword есть метод next(). Таким образом, функция yield-ed 9X_python-interpreter выглядит примерно так:

Исходная версия:

def some_function():
    for i in xrange(4):
        yield i

for i in some_function():
    print i

Это 9X_yield-keyword в основном то, что интерпретатор Python 9X_generator делает с приведенным выше кодом:

class it:
    def __init__(self):
        # Start at -1 so that we get 0 when we add 1 below.
        self.count = -1

    # The __iter__ method will be called once by the 'for' loop.
    # The rest of the magic happens on the object returned by this method.
    # In this case it is the object itself.
    def __iter__(self):
        return self

    # The next method will be called repeatedly by the 'for' loop
    # until it raises StopIteration.
    def next(self):
        self.count += 1
        if self.count < 4:
            return self.count
        else:
            # A StopIteration exception is raised
            # to signal that the iterator is done.
            # This is caught implicitly by the 'for' loop.
            raise StopIteration

def some_func():
    return it()

for i in some_func():
    print i

Чтобы лучше 9X_iterators понять, что происходит за кулисами, цикл 9X_python-interpreter for можно переписать следующим образом:

iterator = some_func()
try:
    while 1:
        print iterator.next()
except StopIteration:
    pass

Это 9X_listiterator имеет больше смысла или вас больше смущает? :)

Я 9X_pythonista должен отметить, что это чрезмерное упрощение 9X_iterator для иллюстративных целей. :)

718
2

  • Я пробовал этот пример в Python 3.6, и если я создам `iterator = some_function()`, переменная `iterator` больше не будет иметь функцию с именем` next() `, а только функци ...

Ответ #4

Ответ на вопрос: Что делает ключевое слово "yield"?

Ключевое слово yield сводится к двум простым 9X_listiterator фактам:

  1. Если компилятор обнаруживает ключевое слово yield где угодно внутри функции, эта функция больше не возвращается с помощью инструкции return. Вместо этого он немедленно возвращает ленивый объект "ожидающего списка", называемый генератором.
  2. Генератор можно повторять. Что такое итерация? Это что-то вроде list или set, range или dict-view со встроенным протоколом для посещения каждого элемента в определенном порядке.

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

generator = myYieldingFunction(...)
x = list(generator)

   generator
       v
[x[0], ..., ???]

         generator
             v
[x[0], x[1], ..., ???]

               generator
                   v
[x[0], x[1], x[2], ..., ???]

                       StopIteration exception
[x[0], x[1], x[2]]     done

list==[x[0], x[1], x[2]]

Пример

Давайте определим 9X_yield-keyword функцию makeRange, которая похожа на range в Python. Вызов 9X_yield makeRange(n) ВОЗВРАЩАЕТ ГЕНЕРАТОР:

def makeRange(n):
    # return 0,1,2,...,n-1
    i = 0
    while i < n:
        yield i
        i += 1

>>> makeRange(5)

Чтобы заставить генератор 9X_python-interpreter немедленно возвращать свои ожидающие значения, вы 9X_iterators можете передать его в list() (так же, как и любой 9X_python-interpreter итерируемый):

>>> list(makeRange(5))
[0, 1, 2, 3, 4]

Пример сравнения с «просто возвращением списка»

Приведенный выше пример можно 9X_python-shell рассматривать как просто создание списка, который 9X_python-shell вы добавляете и возвращаете:

# return a list                  #  # return a generator
def makeRange(n):                #  def makeRange(n):
    """return [0,1,2,...,n-1]""" #      """return 0,1,2,...,n-1"""
    TO_RETURN = []               # 
    i = 0                        #      i = 0
    while i < n:                 #      while i < n:
        TO_RETURN += [i]         #          yield i
        i += 1                   #          i += 1
    return TO_RETURN             # 

>>> makeRange(5)
[0, 1, 2, 3, 4]

Но есть одно 9X_generators существенное отличие; см. последний раздел.


Как можно использовать генераторы

Итерация 9X_iterators - это последняя часть понимания списка, и 9X_coroutine все генераторы являются итеративными, поэтому 9X_generators их часто используют следующим образом:

#                  < ITERABLE >
>>> [x+10 for x in makeRange(5)]
[10, 11, 12, 13, 14]

Чтобы 9X_generators лучше понять генераторы, вы можете поэкспериментировать 9X_listiterator с модулем itertools (обязательно используйте chain.from_iterable, а 9X_python-shell не chain, когда это необходимо). Например, вы 9X_generators можете даже использовать генераторы для 9X_python-shell реализации бесконечно длинных ленивых списков, таких 9X_pythonic как itertools.count(). Вы можете реализовать свой собственный 9X_iterators def enumerate(iterable): zip(count(), iterable) или, альтернативно, сделать это с помощью 9X_iterators ключевого слова yield в цикле while.

Обратите 9X_generator внимание: генераторы на самом деле могут 9X_coroutine использоваться для многих других вещей, таких 9X_iterators как implementing coroutines, недетерминированное программирование 9X_listiterator или другие элегантные вещи. Однако представленная 9X_listiterator здесь точка зрения "ленивых списков" - это 9X_generators наиболее распространенное применение, которое 9X_py вы найдете.


За кадром

Так работает «протокол итераций 9X_pythonista Python». То есть, что происходит, когда 9X_yield-keyword вы выполняете list(makeRange(5)). Это то, что я описал ранее 9X_py как «ленивый, инкрементный список».

>>> x=iter(range(5))
>>> next(x)
0
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> next(x)
4
>>> next(x)
Traceback (most recent call last):
  File "", line 1, in 
StopIteration

Встроенная 9X_python-shell функция next() просто вызывает функцию объектов 9X_pythonic .next(), которая является частью «протокола итераций» и 9X_coroutine присутствует на всех итераторах. Вы можете 9X_generators вручную использовать функцию next() (и другие 9X_generators части итерационного протокола) для реализации 9X_python необычных вещей, обычно в ущерб удобочитаемости, поэтому 9X_coroutine старайтесь этого не делать ...


Детали

Обычно большинство 9X_python-interpreter людей не заботятся о следующих различиях 9X_coroutine и, вероятно, захотят перестать читать здесь.

В 9X_python-shell языке Python итератором является любой объект, который 9X_python-interpreter «понимает концепцию цикла for», например 9X_generators список [1,2,3], а итератор - это конкретный экземпляр запрошенного 9X_py цикла for, например [1,2,3].__iter__(). Генератор точно такой же, как 9X_iterator и любой итератор, за исключением того, как 9X_iterators он был написан (с синтаксисом функции).

Когда 9X_python-interpreter вы запрашиваете итератор из списка, он создает 9X_python новый итератор. Однако, когда вы запрашиваете 9X_iterators итератор у итератора (что вы делаете редко), он 9X_pythonic просто дает вам свою копию.

Таким образом, в 9X_coroutine том маловероятном случае, если вы не сможете 9X_listiterator сделать что-то подобное ...

> x = myRange(5)
> list(x)
[0, 1, 2, 3, 4]
> list(x)
[]

... тогда помните, что 9X_python-shell генератор - это итератор; то есть одноразового использования. Если 9X_py вы хотите использовать его повторно, вам 9X_generators следует снова вызвать myRange(...). Если вам нужно использовать 9X_pythonista результат дважды, преобразуйте результат 9X_coroutine в список и сохраните его в переменной x = list(myRange(5)). Те, кому 9X_python абсолютно необходимо клонировать генератор 9X_iterators (например, кто делает ужасающе хакерское 9X_python метапрограммирование), могут использовать 9X_generator itertools.tee (still works in Python 3) в случае крайней необходимости, поскольку 9X_pythonista copyable iterator Python PEP standards proposal был отложен.

610
0

Ответ #5

Ответ на вопрос: Что делает ключевое слово "yield"?

Что делает ключевое слово yield в Python?

Краткое содержание ответа

  • Функция с yield при вызове возвращает Generator.
  • Генераторы являются итераторами, потому что они реализуют iterator protocol, поэтому вы можете перебирать их.
  • Генератор также может отправлять информацию, что делает его концептуально сопрограммой.
  • В Python 3 вы можете делегировать от одного генератора к другому в обоих направлениях с помощью yield from.
  • (В приложении критикуется пара ответов, включая верхний, и обсуждается использование return в генераторе.)

Генераторы:

yield разрешен только внутри определения функции, а 9X_py включение yield в определение функции заставляет его возвращать генератор.

Идея генераторов пришла из других языков 9X_yield-keyword (см. сноску 1) с различными реализациями. В 9X_generators генераторах Python выполнение кода - frozen в 9X_yield точке yield. Когда вызывается генератор 9X_generator (методы обсуждаются ниже), выполнение возобновляется 9X_python-shell и затем останавливается при следующем выходе.

yield предоставляет простой 9X_generators способ implementing the iterator protocol, определяемый следующими двумя методами: __iter__ и 9X_listiterator next (Python 2) или __next__ (Python 3). Оба эти метода сделать 9X_coroutine объект итератором, который можно было бы 9X_generators проверить с помощью абстрактной базы Iterator Класс 9X_python-interpreter из модуля collections.

>>> def func():
...     yield 'I am'
...     yield 'a generator!'
... 
>>> type(func)                 # A function with yield is still a function

>>> gen = func()
>>> type(gen)                  # but it returns a generator

>>> hasattr(gen, '__iter__')   # that's an iterable
True
>>> hasattr(gen, 'next')       # and with .next (.__next__ in Python 3)
True                           # implements the iterator protocol.

Тип генератора - это подтип итератора:

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True

А 9X_python при необходимости мы можем проверить тип 9X_python-interpreter следующим образом:

>>> isinstance(gen, types.GeneratorType)
True
>>> isinstance(gen, collections.Iterator)
True

Функция Iterator is that once exhausted, вы не можете 9X_listiterator повторно использовать или сбросить ее:

>>> list(gen)
['I am', 'a generator!']
>>> list(gen)
[]

Вам 9X_yield придется сделать еще один, если вы хотите 9X_python снова использовать его функции (см. сноску 9X_yield 2):

>>> list(func())
['I am', 'a generator!']

Данные можно получить программно, например:

def func(an_iterable):
    for item in an_iterable:
        yield item

Вышеупомянутый 9X_python-shell простой генератор также эквивалентен приведенному 9X_yield-keyword ниже - начиная с Python 3.3 (и недоступен 9X_listiterator в Python 2), вы можете использовать yield from:

def func(an_iterable):
    yield from an_iterable

Однако 9X_python-shell yield from также позволяет делегировать субгенераторам, которые 9X_iterators будут объяснены в следующем разделе о совместном 9X_python-interpreter делегировании с подпрограммами.

Сопрограммы:

yield формирует 9X_iterators выражение, которое позволяет отправлять 9X_yield-keyword данные в генератор (см. сноску 3)

Вот пример, обратите 9X_py внимание на переменную received, которая будет указывать 9X_yield-keyword на данные, отправляемые в генератор:

def bank_account(deposited, interest_rate):
    while True:
        calculated_interest = interest_rate * deposited 
        received = yield calculated_interest
        if received:
            deposited += received


>>> my_account = bank_account(1000, .05)

Во-первых, мы 9X_generator должны поставить генератор в очередь с помощью 9X_python встроенной функции next. Так и будет вызвать 9X_pythonista соответствующий метод next или __next__, в зависимости 9X_pythonista от версии Python, который вы используете:

>>> first_year_interest = next(my_account)
>>> first_year_interest
50.0

И 9X_coroutine теперь мы можем отправлять данные в генератор. (Sending None is the same as calling next.):

>>> next_year_interest = my_account.send(first_year_interest + 1000)
>>> next_year_interest
102.5

Совместное делегирование подпрограмм с yield from

Напомним, что 9X_python yield from доступен в Python 3. Это позволяет нам 9X_coroutine делегировать сопрограммы подпрограмме:


def money_manager(expected_rate):
    # must receive deposited value from .send():
    under_management = yield                   # yield None to start.
    while True:
        try:
            additional_investment = yield expected_rate * under_management 
            if additional_investment:
                under_management += additional_investment
        except GeneratorExit:
            '''TODO: write function to send unclaimed funds to state'''
            raise
        finally:
            '''TODO: write function to mail tax info to client'''
        

def investment_account(deposited, manager):
    '''very simple model of an investment account that delegates to a manager'''
    # must queue up manager:
    next(manager)      # <- same as manager.send(None)
    # This is where we send the initial deposit to the manager:
    manager.send(deposited)
    try:
        yield from manager
    except GeneratorExit:
        return manager.close()  # delegate?

И 9X_py теперь мы можем делегировать функциональность 9X_yield подгенератору, и его можно использовать генератором, как 9X_python указано выше:

my_manager = money_manager(.06)
my_account = investment_account(1000, my_manager)
first_year_return = next(my_account) # -> 60.0

Теперь смоделируйте добавление 9X_python-interpreter еще 1000 к счету плюс возврат по счету (60,0):

next_year_return = my_account.send(first_year_return + 1000)
next_year_return # 123.6

Вы 9X_generator можете узнать больше о точной семантике 9X_pythonic yield from в PEP 380.

Другие методы: закрыть и выбросить

Метод close вызывает GeneratorExit в точке, где функция казнь 9X_python-shell была заморожена. Это также будет вызываться 9X_iterator __del__, чтобы вы можно поместить любой код очистки 9X_py там, где вы обрабатываете GeneratorExit:

my_account.close()

Вы также можете 9X_python-shell вызвать исключение, которое может быть обработано 9X_python-interpreter в генераторе. или передается обратно пользователю:

import sys
try:
    raise ValueError
except:
    my_manager.throw(*sys.exc_info())

Повышает:

Traceback (most recent call last):
  File "", line 4, in 
  File "", line 6, in money_manager
  File "", line 2, in 
ValueError

Заключение

Мне 9X_listiterator кажется, я рассмотрел все аспекты следующего 9X_generators вопроса:

Что делает ключевое слово yield в Python?

Оказывается, yield многое делает. Я уверен, что 9X_iterators могу добавить еще подробные примеры к этому. Если 9X_python вы хотите большего или у вас есть конструктивная 9X_iterator критика, дайте мне знать, комментируя ниже.


Приложение:

Критика самого популярного / принятого ответа **

  • Он не понимает, что делает итерируемым, просто используя список в качестве примера. См. Мои ссылки выше, но вкратце: итерация имеет метод __iter__, возвращающий итератор. Итератор предоставляет метод .next (Python 2 или .__next__ (Python 3), который неявно вызывается циклами for до тех пор, пока он не вызовет StopIteration, и как только это произойдет, он будет продолжать делать это.
  • Затем он использует выражение генератора, чтобы описать, что такое генератор. Поскольку генератор - это просто удобный способ создания итератора, он только сбивает с толку, и мы еще не дошли до части yield.
  • В разделе Контроль истощения генератора он вызывает метод .next, тогда как вместо этого он должен использовать встроенную функцию next. Это был бы подходящий уровень косвенного обращения, потому что его код не работает в Python 3.
  • Itertools? Это вообще не имело отношения к тому, что делает yield.
  • Никакого обсуждения методов, которые yield предоставляет вместе с новой функциональностью yield from в Python 3. Самый популярный / принятый ответ - очень неполный ответ.

Критика ответа, предлагающего yield в выражении или понимании генератора.

Грамматика 9X_python в настоящее время допускает любое выражение 9X_yield в понимании списка.

expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
                     ('=' (yield_expr|testlist_star_expr))*)
...
yield_expr: 'yield' [yield_arg]
yield_arg: 'from' test | testlist

Поскольку yield является 9X_pythonic выражением, некоторые считают его интересным 9X_pythonic для использования в понимании или выражении 9X_py генератора, несмотря на то, что здесь не 9X_pythonic упоминается особо удачный вариант использования.

Основными 9X_pythonista разработчиками CPython являются discussing deprecating its allowance. Вот соответствующее 9X_pythonic сообщение из списка рассылки:

30 января 2017 9X_iterators г. в 19:05 Бретт Кэннон написал:

Вс, 29 января 9X_iterator 2017 г., 16:39 Крейг Родригес написал:

Я 9X_listiterator согласен с любым подходом. Оставить вещи 9X_python такими, какие они есть в Python 3 не годится, ИМХО.

Мой 9X_listiterator голос: это SyntaxError, поскольку вы не 9X_yield-keyword получаете того, от чего ожидаете. синтаксис.

Я 9X_generator согласен, что это разумное место для нас, поскольку 9X_yield-keyword любой код полагаться на текущее поведение 9X_python-interpreter действительно слишком умно, чтобы быть ремонтопригодный.

Для 9X_yield этого нам, вероятно, понадобится:

  • SyntaxWarning или DeprecationWarning в 3.7
  • Предупреждение Py3k в 2.7.x
  • SyntaxError в 3.8

Ура, Ник.

- Ник 9X_listiterator Коглан | ncoghlan на gmail.com | Брисбен, Австралия

Кроме 9X_iterators того, существует outstanding issue (10544), который, похоже, указывает 9X_python-shell на то, что это никогда не будет хорошей идеей (PyPy, реализация 9X_pythonic Python, написанная на Python, уже вызывает 9X_coroutine синтаксические предупреждения.)

Итог, пока 9X_yield-keyword разработчики CPython не сообщат нам иное: Не помещайте yield в выражение или понимание генератора.

Оператор return в генераторе

В 9X_pythonista Python 2:

В функции генератора оператор return не может 9X_listiterator включать expression_list. В этом контексте пустой return означает, что 9X_yield генератор готов и вызовет активацию StopIteration.

expression_list - это, по 9X_coroutine сути, любое количество выражений, разделенных 9X_python-interpreter запятыми. По сути, в Python 2 вы можете 9X_python-shell остановить генератор с помощью return, но вы не 9X_pythonic можете вернуть значение.

В Python 3:

В функции генератора 9X_pythonista оператор return указывает, что генератор готов, и 9X_coroutine вызывает вызов StopIteration. Возвращаемое значение (если 9X_generator есть) используется в качестве аргумента 9X_py для создания StopIteration и становится атрибутом StopIteration.value.

Сноски

  1. Языки CLU, Sather и Icon упоминались в предложении познакомить с концепцией генераторов в Python. Общая идея такова что функция может поддерживать внутреннее состояние и давать промежуточные точки данных по запросу пользователя. Это обещал быть superior in performance to other approaches, including Python threading, который даже недоступен в некоторых системах.

  2. Это означает, например, что объекты range не являются объектами Iterator, даже если они итерируемы, потому что их можно использовать повторно. Как и списки, их методы __iter__ возвращают объекты-итераторы.

yield изначально 9X_py был введен как оператор, что означает, что 9X_pythonic он может появляться только в начале строки 9X_generators в блоке кода. Теперь yield создает выражение 9X_listiterator доходности. https://docs.python.org/2/reference/simple_stmts.html#grammar-token-yield_stmt Это изменение было proposed, чтобы 9X_generator позволить пользователю отправлять данные 9X_py в генератор так же, как его можно было получить. Чтобы 9X_python-interpreter отправлять данные, нужно уметь их чему-то 9X_generators назначить, и для этого инструкция просто 9X_python-shell не сработает.

524
0

Ответ #6

Ответ на вопрос: Что делает ключевое слово "yield"?

yield похож на return - он возвращает все, что вы ему 9X_yield скажете (как генератор). Разница в том, что 9X_generators при следующем вызове генератора выполнение 9X_py начинается с последнего вызова оператора 9X_python-interpreter yield. В отличие от return, кадр стека не очищается при возникновении yield, однако управление передается обратно вызывающей стороне, поэтому его состояние возобновится при следующем вызове функции.

В случае вашего кода 9X_yield функция get_child_candidates действует как итератор, поэтому 9X_generator при расширении списка он добавляет по одному 9X_python элементу в новый список.

list.extend вызывает итератор, пока 9X_py он не будет исчерпан. В случае опубликованного 9X_listiterator вами образца кода было бы гораздо проще 9X_listiterator просто вернуть кортеж и добавить его в список.

428
1

  • Это близко, но неверно. Каждый раз, когда вы вызываете функцию с выражением yield в ней, она возвращает новый объект-генератор. Только когда вы вызываете метод этого генератора .next(), выполнение возобновляется ...

Ответ #7

Ответ на вопрос: Что делает ключевое слово "yield"?

Следует упомянуть еще одну вещь: функция, которая 9X_yield дает результат, на самом деле не должна 9X_listiterator завершаться. Я написал такой код:

def fib():
    last, cur = 0, 1
    while True: 
        yield cur
        last, cur = cur, last + cur

Тогда я 9X_generators могу использовать его в другом коде, например:

for f in fib():
    if some_condition: break
    coolfuncs(f);

Это 9X_yield действительно помогает упростить некоторые 9X_generators проблемы и упрощает работу с некоторыми 9X_yield вещами.

303
0

Ответ #8

Ответ на вопрос: Что делает ключевое слово "yield"?

Для тех, кто предпочитает минималистичный 9X_python рабочий пример, подумайте об этом интерактивном 9X_python-interpreter сеансе Python:

>>> def f():
...   yield 1
...   yield 2
...   yield 3
... 
>>> g = f()
>>> for i in g:
...   print(i)
... 
1
2
3
>>> for i in g:
...   print(i)
... 
>>> # Note that this time nothing was printed

295
0

Ответ #9

Ответ на вопрос: Что делает ключевое слово "yield"?

TL; DR

Вместо этого:

def square_list(n):
    the_list = []                         # Replace
    for x in range(n):
        y = x * x
        the_list.append(y)                # these
    return the_list                       # lines

сделайте это:

def square_yield(n):
    for x in range(n):
        y = x * x
        yield y                           # with this one.

Когда вы создаете список с нуля, yield вместо 9X_python-shell этого используйте каждую часть.

Это был мой 9X_listiterator первый "ага" момент с yield.


yield - это 9X_python-interpreter sugary способ сказать

создать серию вещей

Такое 9X_iterators же поведение:

>>> for square in square_list(4):
...     print(square)
...
0
1
4
9
>>> for square in square_yield(4):
...     print(square)
...
0
1
4
9

Другое поведение:

Результат 9X_pythonista - однопроходный: можно выполнить итерацию только один 9X_iterators раз. Когда функция имеет yield, мы называем 9X_iterators ее generator function. И iterator - это то, что он возвращает. Эти 9X_pythonista термины показательны. Мы теряем удобство 9X_yield контейнера, но получаем силу ряда, вычисляемого 9X_generator по мере необходимости и произвольно длинной.

Доходность 9X_generators ленивая, она откладывает вычисления. Функция с 9X_generators yield в ней на самом деле вообще не выполняется, когда вы ее вызываете. Она возвращает iterator object, который запоминает, где 9X_coroutine она остановилась. Каждый раз, когда вы вызываете 9X_python-interpreter next() на итераторе (это происходит в цикле for), выполнение 9X_pythonic смещается вперед до следующего yield. return вызывает 9X_python-shell StopIteration и завершает серию (это естественный 9X_coroutine конец цикла for).

Доходность разносторонняя. Данные не 9X_yield нужно хранить все вместе, их можно делать 9X_listiterator доступными по отдельности. Оно может быть 9X_generator бесконечным.

>>> def squares_all_of_them():
...     x = 0
...     while True:
...         yield x * x
...         x += 1
...
>>> squares = squares_all_of_them()
>>> for _ in range(4):
...     print(next(squares))
...
0
1
4
9

Если вам нужно несколько проходов и серия не слишком 9X_generator длинная, просто вызовите для нее list():

>>> list(square_yield(4))
[0, 1, 4, 9]

Отличный 9X_python-shell выбор слова yield, потому что both meanings применимо:

yield - производить 9X_python или обеспечивать (как в сельском хозяйстве)

... укажите 9X_iterators следующие данные в серии.

yield - уступить дорогу 9X_iterators или отказаться (как в случае политической 9X_python власти)

... прекращать выполнение ЦП, пока 9X_coroutine итератор не продвинется вперед.

275
0

Ответ #10

Ответ на вопрос: Что делает ключевое слово "yield"?

Yield дает вам генератор.

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

bar.next()
1
bar.next()
3
bar.next()
5

Как видите, в первом 9X_yield случае foo хранит в памяти сразу весь список. Это 9X_pythonista не имеет большого значения для списка из 9X_generators 5 элементов, но что, если вам нужен список 9X_iterator из 5 миллионов? Это не только пожирает много 9X_listiterator памяти, но и требует много времени для создания 9X_python-interpreter во время вызова функции.

Во втором случае 9X_coroutine bar просто предоставляет вам генератор. Генератор 9X_pythonista является итеративным - это означает, что 9X_python-shell вы можете использовать его в цикле for и т. Д., Но 9X_python-interpreter к каждому значению можно получить доступ 9X_python только один раз. Все значения также не сохраняются 9X_pythonista в памяти одновременно; объект-генератор 9X_pythonic «запоминает», где он был в цикле в последний 9X_pythonista раз, когда вы его вызывали - таким образом, если 9X_pythonic вы используете итерацию для (скажем) подсчета 9X_python-interpreter до 50 миллиардов, вам не нужно считать до 9X_generators 50 миллиардов все сразу и сохраните 50 миллиардов 9X_coroutine чисел для подсчета.

Опять же, это довольно 9X_listiterator надуманный пример. Вы, вероятно, использовали 9X_python-shell бы itertools, если бы действительно хотели 9X_python-shell сосчитать до 50 миллиардов. :)

Это самый 9X_yield-keyword простой вариант использования генераторов. Как 9X_python-interpreter вы сказали, его можно использовать для написания 9X_python эффективных перестановок, используя yield 9X_python для проталкивания вещей через стек вызовов 9X_generators вместо использования какой-либо переменной 9X_iterator стека. Генераторы также могут использоваться 9X_python-shell для специализированного обхода дерева и 9X_py для многих других вещей.

236
1

  • Просто примечание - в Python 3 `range` также возвращает генератор вместо списка, по ...

Ответ #11

Ответ на вопрос: Что делает ключевое слово "yield"?

Возвращает генератор. Я не особо знаком 9X_yield-keyword с Python, но считаю, что это то же самое, что 9X_python и C#'s iterator blocks, если вы знакомы с ними.

Ключевая идея 9X_generator состоит в том, что компилятор / интерпретатор 9X_yield-keyword / что-то еще делает некоторую уловку, так 9X_yield-keyword что, что касается вызывающего абонента, он 9X_iterators может продолжать вызывать next(), и он будет 9X_python-interpreter продолжать возвращать значения - , как если бы метод генератора был приостановлено. Теперь 9X_listiterator очевидно, что вы не можете действительно 9X_coroutine «приостановить» метод, поэтому компилятор 9X_python-shell создает конечный автомат, чтобы вы запомнили, где 9X_py вы сейчас находитесь, как выглядят локальные 9X_yield-keyword переменные и т. Д. Это намного проще, чем 9X_iterators написать итератор самостоятельно.

232
0

Ответ #12

Ответ на вопрос: Что делает ключевое слово "yield"?

Есть один тип ответа, который, как мне кажется, еще 9X_python не дан, среди множества отличных ответов, описывающих, как 9X_py использовать генераторы. Вот ответ теории 9X_yield-keyword языка программирования:

Оператор yield в Python 9X_iterators возвращает генератор. Генератор в Python 9X_python-shell - это функция, которая возвращает продолжения (и, в 9X_pythonista частности, тип сопрограммы, но продолжения 9X_iterator представляют собой более общий механизм 9X_coroutine для понимания того, что происходит).

Продолжения 9X_pythonic в теории языков программирования - это гораздо 9X_pythonista более фундаментальный вид вычислений, но 9X_python-interpreter они используются не часто, потому что их 9X_python чрезвычайно сложно осмыслить, а также очень 9X_coroutine сложно реализовать. Но идея того, что такое 9X_yield продолжение, проста: это состояние вычисления, которое 9X_iterators еще не завершено. В этом состоянии сохраняются 9X_coroutine текущие значения переменных, операции, которые 9X_coroutine еще предстоит выполнить, и так далее. Затем 9X_yield-keyword в какой-то момент позже в программе может 9X_generators быть вызвано продолжение, так что переменные 9X_python-shell программы будут сброшены в это состояние 9X_python и будут выполнены операции, которые были 9X_python-shell сохранены.

Продолжение в этой более общей 9X_generators форме может быть реализовано двумя способами. В 9X_iterator способе call/cc стек программы буквально сохраняется, а 9X_yield затем, когда вызывается продолжение, стек 9X_yield-keyword восстанавливается.

В стиле передачи продолжения 9X_python-shell (CPS) продолжения - это просто обычные функции 9X_pythonista (только в языках, где функции являются первоклассными), которыми 9X_python программист явно управляет и передает подпрограммам. В 9X_yield-keyword этом стиле состояние программы представлено 9X_yield замыканиями (и переменными, которые в них 9X_yield-keyword закодированы), а не переменными, которые 9X_pythonic находятся где-то в стеке. Функции, управляющие 9X_python-shell потоком управления, принимают продолжение 9X_python-shell в качестве аргументов (в некоторых вариантах 9X_yield-keyword CPS функции могут принимать несколько продолжений) и 9X_generator манипулируют потоком управления, вызывая 9X_listiterator их, просто вызывая их и возвращая впоследствии. Вот 9X_python очень простой пример стиля передачи продолжения:

def save_file(filename):
  def write_file_continuation():
    write_stuff_to_file(filename)

  check_if_file_exists_and_user_wants_to_overwrite(write_file_continuation)

В 9X_listiterator этом (очень упрощенном) примере программист 9X_python сохраняет операцию фактической записи файла 9X_coroutine в продолжение (что потенциально может быть 9X_py очень сложной операцией, требующей записи 9X_python-shell множества деталей), а затем передает это 9X_listiterator продолжение (т. е. как первоклассное закрытие) другому 9X_yield оператору, который выполняет дополнительную 9X_python обработку, а затем вызывает его при необходимости. (Я 9X_generators часто использую этот шаблон проектирования 9X_py в реальном программировании графического 9X_python-shell интерфейса пользователя, либо потому, что 9X_yield он экономит мне строки кода, либо, что более 9X_generators важно, для управления потоком управления 9X_python-interpreter после срабатывания событий графического 9X_yield интерфейса.)

Остальная часть этого поста 9X_yield-keyword будет, без потери общности, концептуализировать 9X_iterator продолжения как CPS, потому что это чертовски 9X_yield легче понять и прочитать.


Теперь поговорим 9X_python-shell о генераторах в Python. Генераторы - это 9X_yield особый подтип продолжения. В то время как 9X_pythonista продолжения обычно могут сохранять состояние вычисления (т. Е. Стек вызовов программы), генераторы могут сохранять состояние итерации только на итератор. Хотя 9X_yield-keyword это определение немного вводит в заблуждение 9X_listiterator для некоторых случаев использования генераторов. Например:

def f():
  while True:
    yield 4

Очевидно, что 9X_python-interpreter это разумная итерация, поведение которой 9X_iterator четко определено - каждый раз, когда генератор 9X_coroutine выполняет итерацию, он возвращает 4 (и делает 9X_pythonista это всегда). Но, вероятно, при мысли об 9X_py итераторах приходит на ум не прототип итерируемого 9X_py объекта (т. Е. for x in collection: do_something(x)). Этот пример иллюстрирует 9X_yield возможности генераторов: если что-то является 9X_python-interpreter итератором, генератор может сохранить состояние 9X_yield-keyword своей итерации.

Повторюсь: продолжения могут 9X_yield сохранять состояние стека программы, а генераторы 9X_coroutine могут сохранять состояние итерации. Это 9X_yield-keyword означает, что продолжения намного мощнее 9X_py генераторов, но также и генераторы намного 9X_generator проще. Разработчику языка их легче реализовать, и 9X_python-shell программисту легче их использовать (если 9X_iterators у вас есть время, чтобы записать, попробуйте 9X_generator прочитать и понять this page about continuations and call/cc).

Но вы можете легко 9X_py реализовать (и концептуализировать) генераторы 9X_python-interpreter как простой, конкретный случай стиля передачи 9X_python-interpreter продолжения:

Каждый раз, когда вызывается 9X_python-interpreter yield, он сообщает функции, что нужно вернуть 9X_pythonic продолжение. Когда функция вызывается снова, она 9X_python-interpreter начинается с того места, где была остановлена. Итак, в 9X_python-interpreter псевдопсевдокоде (т.е. не в псевдокоде, но 9X_listiterator не в коде) метод генератора next в основном 9X_py выглядит следующим образом:

class Generator():
  def __init__(self,iterable,generatorfun):
    self.next_continuation = lambda:generatorfun(iterable)

  def next(self):
    value, next_continuation = self.next_continuation()
    self.next_continuation = next_continuation
    return value

где ключевое 9X_py слово yield на самом деле является синтаксическим 9X_pythonic сахаром для реальной функции генератора, в 9X_generator основном что-то вроде:

def generatorfun(iterable):
  if len(iterable) == 0:
    raise StopIteration
  else:
    return (iterable[0], lambda:generatorfun(iterable[1:]))

Помните, что это всего 9X_pythonic лишь псевдокод, и фактическая реализация 9X_iterator генераторов в Python более сложна. Но в 9X_pythonista качестве упражнения, чтобы понять, что происходит, попробуйте 9X_yield-keyword использовать стиль передачи продолжения 9X_iterators для реализации объектов генератора без использования 9X_pythonic ключевого слова yield.

206
0

Ответ #13

Ответ на вопрос: Что делает ключевое слово "yield"?

Вот пример на простом языке. Я покажу соответствие 9X_coroutine между высокоуровневыми человеческими концепциями 9X_py и низкоуровневыми концепциями Python.

Я хочу 9X_iterators оперировать последовательностью чисел, но 9X_iterators не хочу утруждать себя созданием этой последовательности, я 9X_python-interpreter хочу сосредоточиться только на операции, которую 9X_pythonic хочу выполнить. Итак, делаю следующее:

  • Я звоню вам и говорю, что мне нужна определенная последовательность чисел, и я сообщаю вам, каков алгоритм.
    Этот шаг соответствует def включению функции генератора, то есть функции, содержащей yield.
  • Через некоторое время я скажу вам: «Хорошо, приготовьтесь назвать мне последовательность цифр».
    Этот шаг соответствует вызову функции генератора, которая возвращает объект-генератор. Обратите внимание, что вы пока не называете мне никаких чисел; вы просто берете бумагу и карандаш.
  • Я прошу вас: «Назовите мне следующий номер», а вы назовите мне первое число; после этого вы ждете, пока я спрошу у вас следующий номер. Ваша задача - вспомнить, где вы были, какие числа вы уже сказали и какое будет следующее число. Меня не волнуют детали.
    Этот шаг соответствует вызову .next() для объекта-генератора.
  • … повторять предыдущий шаг, пока…
  • в конце концов, вам может прийти конец. Вы не называете мне номер; вы просто кричите: «Держите лошадей! Я закончил! Больше никаких номеров!»
    Этот шаг соответствует завершению работы объекта-генератором и возникновению исключения StopIteration . Функция генератора не должна вызывать исключение. Он возникает автоматически, когда функция завершается или выдает return.

Это 9X_iterators то, что делает генератор (функция, содержащая 9X_yield yield); он начинает выполнение, приостанавливается 9X_python всякий раз, когда он выполняет yield, а при запросе 9X_coroutine значения .next() он продолжает с того места, где 9X_generators он был последним. Он идеально сочетается 9X_generator по дизайну с протоколом итератора Python, который 9X_generators описывает, как последовательно запрашивать 9X_yield значения.

Самым известным пользователем протокола 9X_iterators итератора является команда for в Python. Итак, всякий 9X_pythonic раз, когда вы делаете:

for item in sequence:

не имеет значения, является 9X_python-shell ли sequence списком, строкой, словарем или генератором 9X_generator объектом, как описано выше; результат тот же: вы 9X_iterators читаете элементы последовательно один за 9X_py другим.

Обратите внимание, что def создание 9X_listiterator функции, содержащей ключевое слово yield, - не 9X_yield-keyword единственный способ создать генератор; это 9X_iterators самый простой способ создать его.

Для получения 9X_python-shell более точной информации прочтите о iterator types, yield statement и 9X_iterators generators в документации Python.

205
0

Ответ #14

Ответ на вопрос: Что делает ключевое слово "yield"?

Хотя многие ответы показывают, почему вы 9X_listiterator должны использовать yield для создания генератора, для 9X_python-shell yield есть больше применений. Создать сопрограмму, которая 9X_python-shell позволяет передавать информацию между двумя 9X_python-shell блоками кода, довольно просто. Я не буду 9X_pythonic повторять какие-либо из уже приведенных 9X_generator прекрасных примеров использования yield для создания 9X_yield-keyword генератора.

Чтобы понять, что делает yield в следующем 9X_python-interpreter коде, вы можете пальцем проследить цикл 9X_python через любой код, имеющий yield. Каждый раз, когда 9X_coroutine ваш палец касается yield, вы должны ждать ввода 9X_pythonista next или send. Когда вызывается next, вы отслеживаете 9X_python код до тех пор, пока не нажмете yield ... код 9X_python справа от yield оценивается и возвращается вызывающей 9X_generator стороне ... затем вы ждете. Когда next вызывается 9X_yield-keyword снова, вы выполняете еще один цикл по коду. Однако 9X_pythonic вы заметите, что в сопрограмме yield также можно 9X_python-interpreter использовать с send…, который отправит значение 9X_python от вызывающего в функцию вывода. Если задан 9X_python-shell send, тогда yield получает отправленное значение 9X_python-interpreter и выплевывает его в левую часть ... тогда 9X_yield-keyword трассировка кода продолжается до тех пор, пока 9X_iterators вы снова не нажмете yield (возвращая значение 9X_pythonista в конце, как если был вызван next).

Например:

>>> def coroutine():
...     i = -1
...     while True:
...         i += 1
...         val = (yield i)
...         print("Received %s" % val)
...
>>> sequence = coroutine()
>>> sequence.next()
0
>>> sequence.next()
Received None
1
>>> sequence.send('hello')
Received hello
2
>>> sequence.close()

161
1

  • Милый! [Батут] (https://en.wikipedia.org/wiki/Trampoline_ (вычисления)) (в смысле Лиспа). Нечасто их видишь!<p ...

Ответ #15

Ответ на вопрос: Что делает ключевое слово "yield"?

Существует еще одно использование и значение 9X_python-shell yield (начиная с Python 3.3):

yield from 

От PEP 380 -- Syntax for Delegating to a Subgenerator:

Синтаксис предлагается 9X_generators генератору делегировать часть своих операций 9X_yield другому генератору. Это позволяет выделить 9X_coroutine часть кода, содержащую «yield», и поместить 9X_py ее в другой генератор. Кроме того, подгенератору 9X_iterator разрешено возвращать значение, и значение 9X_coroutine становится доступным для делегирующего генератора.

Новый 9X_coroutine синтаксис также открывает некоторые возможности 9X_generators для оптимизации, когда один генератор повторно 9X_python-interpreter возвращает значения, созданные другим.

Кроме 9X_generator того, this представит (начиная с Python 3.5):

async def new_coroutine(data):
   ...
   await blocking_action()

, чтобы 9X_generator не путать сопрограммы с обычным генератором 9X_pythonista (сегодня yield используется в обоих).

157
0

Ответ #16

Ответ на вопрос: Что делает ключевое слово "yield"?

Все ответы отличные, но для новичков это 9X_listiterator немного сложно.

Я полагаю, вы выучили инструкцию 9X_python return.

По аналогии, return и yield - близнецы. return означает 9X_python-interpreter «возврат и остановка», тогда как «yield» означает 9X_pythonic «вернуться, но продолжить»

  1. Попробуйте получить num_list с помощью return.
def num_list(n):
    for i in range(n):
        return i

Запустить:

In [5]: num_list(3)
Out[5]: 0

Видите 9X_iterator ли, вы получаете только одно число, а не 9X_generators их список. return никогда не позволяет вам успешно 9X_generators преобладать, просто реализует один раз и 9X_listiterator выходит.

  1. Приходит yield

Замените return на yield:

In [10]: def num_list(n):
    ...:     for i in range(n):
    ...:         yield i
    ...:

In [11]: num_list(3)
Out[11]: 

In [12]: list(num_list(3))
Out[12]: [0, 1, 2]

Теперь вы выигрываете, чтобы 9X_generators получить все числа.

По сравнению с return, который 9X_yield-keyword запускается один раз и останавливается, yield выполняется 9X_listiterator запланированное вами время. Вы можете интерпретировать 9X_python-interpreter return как return one of them, а yield как return all of them. Это называется iterable.

  1. Еще один шаг: мы можем переписать инструкцию yield с помощью return
In [15]: def num_list(n):
    ...:     result = []
    ...:     for i in range(n):
    ...:         result.append(i)
    ...:     return result

In [16]: num_list(3)
Out[16]: [0, 1, 2]

Это суть 9X_coroutine yield.

Разница между выводом списка return и выводом 9X_python объекта yield заключается в следующем:

Вы всегда 9X_iterators будете получать [0, 1, 2] из объекта списка, но 9X_py сможете получить их только один раз из «вывода 9X_yield объекта yield». Итак, у него новое имя объекта 9X_py generator, как показано в Out[11]: .

В заключение, как метафора 9X_generator для понимания:

  • return и yield - близнецы
  • list и generator - близнецы

145
1

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

Ответ #17

Ответ на вопрос: Что делает ключевое слово "yield"?

С точки зрения программирования итераторы 9X_iterator реализованы как thunks.

Чтобы реализовать итераторы, генераторы 9X_pythonista и пулы потоков для параллельного выполнения 9X_pythonic и т. д. в качестве преобразователей, используется 9X_generators messages sent to a closure object, у которого есть диспетчер, и dispatcher answers to "messages".

"next" - это сообщение, отправленное 9X_yield в закрытие, созданное вызовом «iter».

Есть много 9X_coroutine способов реализовать это вычисление. Я использовал 9X_coroutine мутацию, но можно выполнить такой вид вычислений 9X_listiterator без мутации, вернув текущее значение и следующий 9X_python выход (сделав его referential transparent). Racket использует последовательность 9X_pythonista преобразований исходной программы на некоторых 9X_python языках-посредниках, одно из таких переписываний 9X_coroutine приводит к преобразованию оператора yield 9X_generator на некотором языке с помощью более простых 9X_python-shell операторов.

Вот демонстрация того, как можно 9X_coroutine переписать yield, который использует структуру 9X_iterator R6RS, но семантика идентична семантике Python. Это 9X_listiterator та же модель вычислений, и требуется только 9X_listiterator изменение синтаксиса, чтобы переписать ее 9X_generators с использованием yield of Python.

Welcome to Racket v6.5.0.3.

-> (define gen
     (lambda (l)
       (define yield
         (lambda ()
           (if (null? l)
               'END
               (let ((v (car l)))
                 (set! l (cdr l))
                 v))))
       (lambda(m)
         (case m
           ('yield (yield))
           ('init  (lambda (data)
                     (set! l data)
                     'OK))))))
-> (define stream (gen '(1 2 3)))
-> (stream 'yield)
1
-> (stream 'yield)
2
-> (stream 'yield)
3
-> (stream 'yield)
'END
-> ((stream 'init) '(a b))
'OK
-> (stream 'yield)
'a
-> (stream 'yield)
'b
-> (stream 'yield)
'END
-> (stream 'yield)
'END
->

132
0

Ответ #18

Ответ на вопрос: Что делает ключевое слово "yield"?

Вот несколько примеров Python того, как 9X_py на самом деле реализовать генераторы, как 9X_python-shell если бы Python не предоставлял для них синтаксический 9X_listiterator сахар:

Как генератор Python:

from itertools import islice

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

assert [1, 1, 2, 3, 5] == list(islice(fib_gen(), 5))

Использование лексических замыканий вместо генераторов

def ftake(fnext, last):
    return [fnext() for _ in xrange(last)]

def fib_gen2():
    #funky scope due to python2.x workaround
    #for python 3.x use nonlocal
    def _():
        _.a, _.b = _.b, _.a + _.b
        return _.a
    _.a, _.b = 0, 1
    return _

assert [1,1,2,3,5] == ftake(fib_gen2(), 5)

Использование замыканий объектов вместо генераторов (потому что ClosuresAndObjectsAreEquivalent)

class fib_gen3:
    def __init__(self):
        self.a, self.b = 1, 1

    def __call__(self):
        r = self.a
        self.a, self.b = self.b, self.a + self.b
        return r

assert [1,1,2,3,5] == ftake(fib_gen3(), 5)

126
0

Ответ #19

Ответ на вопрос: Что делает ключевое слово "yield"?

Я собирался опубликовать «прочтите 19-ю 9X_yield-keyword страницу книги Бизли« Python: Essential 9X_python-interpreter Reference »для быстрого описания генераторов», но 9X_py многие другие уже опубликовали хорошие описания.

Также 9X_iterators обратите внимание, что yield может использоваться 9X_yield в сопрограммах как двойное средство их использования 9X_yield-keyword в функциях генератора. Хотя это не то же 9X_pythonic самое, что и ваш фрагмент кода, (yield) можно использовать 9X_pythonic как выражение в функции. Когда вызывающий 9X_python-shell отправляет значение методу с помощью метода 9X_python-shell send(), сопрограмма будет выполняться до тех пор, пока 9X_python-shell не встретится следующий оператор (yield).

Генераторы 9X_pythonic и сопрограммы - отличный способ настроить 9X_generator приложения, работающие с потоками данных. Я 9X_listiterator подумал, что стоит узнать о другом использовании 9X_yield-keyword оператора yield в функциях.

115
0

Ответ #20

Ответ на вопрос: Что делает ключевое слово "yield"?

Вот простой пример:

def isPrimeNumber(n):
    print "isPrimeNumber({}) call".format(n)
    if n==1:
        return False
    for x in range(2,n):
        if n % x == 0:
            return False
    return True

def primes (n=1):
    while(True):
        print "loop step ---------------- {}".format(n)
        if isPrimeNumber(n): yield n
        n += 1

for n in primes():
    if n> 10:break
    print "wiriting result {}".format(n)

Вывод:

loop step ---------------- 1
isPrimeNumber(1) call
loop step ---------------- 2
isPrimeNumber(2) call
loop step ---------------- 3
isPrimeNumber(3) call
wiriting result 3
loop step ---------------- 4
isPrimeNumber(4) call
loop step ---------------- 5
isPrimeNumber(5) call
wiriting result 5
loop step ---------------- 6
isPrimeNumber(6) call
loop step ---------------- 7
isPrimeNumber(7) call
wiriting result 7
loop step ---------------- 8
isPrimeNumber(8) call
loop step ---------------- 9
isPrimeNumber(9) call
loop step ---------------- 10
isPrimeNumber(10) call
loop step ---------------- 11
isPrimeNumber(11) call

Я не разработчик 9X_python-shell Python, но мне кажется, что yield содержит позицию 9X_python-shell потока программы, и следующий цикл начинается 9X_python с позиции "yield". Кажется, что он ждет 9X_pythonic в этой позиции, а непосредственно перед 9X_listiterator этим возвращает значение снаружи и в следующий 9X_yield раз продолжает работать.

Это интересная и 9X_pythonic приятная способность: D

99
0

Ответ #21

Ответ на вопрос: Что делает ключевое слово "yield"?

Вот мысленный образ того, что делает yield.

Мне 9X_py нравится думать о потоке как о стеке (даже 9X_coroutine если он не реализован таким образом).

Когда 9X_yield вызывается обычная функция, она помещает 9X_yield-keyword свои локальные переменные в стек, выполняет 9X_iterators некоторые вычисления, затем очищает стек 9X_iterator и возвращается. Значения его локальных переменных 9X_pythonista больше никогда не видны.

С функцией yield, когда 9X_generators ее код начинает выполняться (т.е. после 9X_python-interpreter вызова функции, возвращающей объект-генератор, чей 9X_iterators метод next() затем вызывается), она аналогичным 9X_generator образом помещает свои локальные переменные 9X_iterator в стек и вычисляет некоторое время. Но затем, когда 9X_listiterator он попадает в оператор yield, перед очисткой 9X_iterator своей части стека и возвратом он делает 9X_pythonic снимок своих локальных переменных и сохраняет 9X_yield их в объекте-генераторе. Он также записывает 9X_py текущее место в своем коде (т.е. конкретный 9X_python-interpreter оператор yield).

Так что это своего рода замороженная 9X_pythonic функция, на которой висит генератор.

Когда 9X_python-interpreter next() вызывается впоследствии, он извлекает принадлежность 9X_py функции в стек и повторно анимирует ее. Функция 9X_pythonista продолжает вычисление с того места, где 9X_py остановилась, не обращая внимания на тот 9X_iterators факт, что она только что провела целую вечность 9X_coroutine в холодном хранилище.

Сравните следующие 9X_coroutine примеры:

def normalFunction():
    return
    if False:
        pass

def yielderFunction():
    return
    if False:
        yield 12

Когда мы вызываем вторую функцию, она 9X_listiterator ведет себя совсем не так, как первая. Оператор 9X_python yield может быть недоступен, но если он где-то 9X_iterator присутствует, он меняет характер того, с 9X_iterator чем мы имеем дело.

>>> yielderFunction()

Вызов yielderFunction() не запускает его 9X_yield код, а создает генератор из кода. (Возможно, стоит 9X_yield-keyword называть такие вещи префиксом yielder для удобства 9X_generators чтения.)

>>> gen = yielderFunction()
>>> dir(gen)
['__class__',
 ...
 '__iter__',    #Returns gen itself, to make it work uniformly with containers
 ...            #when given to a for loop. (Containers return an iterator instead.)
 'close',
 'gi_code',
 'gi_frame',
 'gi_running',
 'next',        #The method that runs the function's body.
 'send',
 'throw']

В полях gi_code и gi_frame хранится замороженное 9X_yield состояние. Изучая их с помощью dir(..), мы можем 9X_python-interpreter подтвердить, что наша ментальная модель, приведенная 9X_pythonista выше, заслуживает доверия.

85
0

Ответ #22

Ответ на вопрос: Что делает ключевое слово "yield"?

Аналогия может помочь понять эту идею здесь:9X_Что делает ключевое слово "yield"?_iterator

Представьте, что 9X_iterator вы создали замечательную машину, способную 9X_pythonic генерировать тысячи и тысячи лампочек в 9X_python день. Машина производит эти лампочки в коробках 9X_yield с уникальным серийным номером. У вас недостаточно 9X_generator места для одновременного хранения всех этих 9X_coroutine лампочек (т. Е. Вы не можете успевать из-за 9X_python-interpreter ограничений по хранению), поэтому вы хотели 9X_generator бы настроить его для создания лампочек по 9X_generators запросу.

Генераторы Python не сильно отличаются 9X_listiterator от этой концепции.

Представьте, что у вас 9X_yield-keyword есть функция x, которая генерирует уникальные 9X_generator серийные номера для ящиков. Очевидно, у 9X_yield-keyword вас может быть огромное количество таких 9X_python штрих-кодов, сгенерированных функцией. Более 9X_iterators разумным и эффективным вариантом является 9X_python-shell создание серийных номеров по запросу.

Машинный 9X_iterators код:

def barcode_generator():
    serial_number = 10000  # Initial barcode
    while True:
        yield serial_number
        serial_number += 1


barcode = barcode_generator()
while True:
    number_of_lightbulbs_to_generate = int(input("How many lightbulbs to generate? "))
    barcodes = [next(barcode) for _ in range(number_of_lightbulbs_to_generate)]
    print(barcodes)

    # function_to_create_the_next_batch_of_lightbulbs(barcodes)

    produce_more = input("Produce more? [Y/n]: ")
    if produce_more == "n":
        break

Как видите, у нас есть автономная «функция» для 9X_python-shell генерации следующего уникального серийного 9X_python номера каждый раз. Эта функция возвращает 9X_yield-keyword генератор! Как видите, мы не вызываем функцию 9X_coroutine каждый раз, когда нам нужен новый серийный 9X_python-interpreter номер, но мы используем next(), заданный генератору, для 9X_yield получения следующего серийного номера.

Вывод:

How many lightbulbs to generate? 5
[10000, 10001, 10002, 10003, 10004]
Produce more? [Y/n]: y
How many lightbulbs to generate? 6
[10005, 10006, 10007, 10008, 10009, 10010]
Produce more? [Y/n]: y
How many lightbulbs to generate? 7
[10011, 10012, 10013, 10014, 10015, 10016, 10017]
Produce more? [Y/n]: n

82
0

Ответ #23

Ответ на вопрос: Что делает ключевое слово "yield"?

Простой пример, чтобы понять, что это такое: yield

def f123():
    for _ in range(4):
        yield 1
        yield 2


for i in f123():
    print (i)

Результат:

1 2 1 2 1 2 1 2

9X_iterator

77
2

  • @ user9074332, Вы правы, но написано в одну строчку ...

Ответ #24

Ответ на вопрос: Что делает ключевое слово "yield"?

Как следует из каждого ответа, yield используется 9X_py для создания генератора последовательности. Он 9X_generators используется для динамической генерации 9X_coroutine некоторой последовательности. Например, при 9X_generators построчном чтении файла по сети вы можете 9X_pythonic использовать функцию yield следующим образом:

def getNextLines():
   while con.isOpen():
       yield con.read()

Вы 9X_pythonic можете использовать его в своем коде следующим 9X_pythonic образом:

for line in getNextLines():
    doSomeThing(line)

Попытка передать управление выполнением

Управление выполнением будет передано 9X_pythonista из getNextLines() в цикл for при выполнении 9X_python yield. Таким образом, каждый раз, когда 9X_py вызывается getNextLines(), выполнение начинается 9X_py с того места, где оно было приостановлено 9X_yield в прошлый раз.

Короче говоря, функция со 9X_coroutine следующим кодом

def simpleYield():
    yield "first time"
    yield "second time"
    yield "third time"
    yield "Now some useful value {}".format(12)

for i in simpleYield():
    print i

напечатает

"first time"
"second time"
"third time"
"Now some useful value 12"

73
0

Ответ #25

Ответ на вопрос: Что делает ключевое слово "yield"?

(Мой ответ ниже говорит только с точки зрения 9X_pythonista использования генератора Python, а не с 9X_pythonic точки зрения underlying implementation of generator mechanism, который включает некоторые 9X_listiterator уловки манипулирования стеком и кучей.)

Когда 9X_pythonista yield используется вместо return в функции Python, эта 9X_coroutine функция превращается в нечто особенное, называемое 9X_generator generator function. Эта функция вернет объект типа generator. Ключевое слово yield - это флаг, уведомляющий компилятор python о необходимости особого обращения с такой функцией. Обычные 9X_python-interpreter функции завершаются после того, как от него 9X_iterator будет возвращено какое-либо значение. Но 9X_generators с помощью компилятора функцию генератора 9X_coroutine можно рассматривать как возобновляемую. То есть контекст выполнения 9X_iterator будет восстановлен, и выполнение продолжится 9X_pythonic с последнего запуска. Пока вы явно не вызовете 9X_generators return, что вызовет исключение StopIteration (которое 9X_yield-keyword также является частью протокола итератора) или 9X_yield не достигнет конца функции. Я нашел много 9X_iterators ссылок на generator, но этот one из functional programming perspective является наиболее 9X_pythonic усваиваемым.

(Теперь я хочу поговорить о 9X_pythonic причинах generator и iterator на основе моего собственного 9X_yield-keyword понимания. Надеюсь, это поможет вам понять 9X_generator существенную мотивацию итератора и генератора. Такая концепция 9X_py присутствует и в других языках, например 9X_iterator в C#.)

Насколько я понимаю, когда мы хотим 9X_coroutine обработать кучу данных, мы обычно сначала 9X_python где-то храним данные, а затем обрабатываем 9X_python-interpreter их по очереди. Но этот наивный подход проблематичен. Если 9X_py объем данных огромен, заранее хранить их 9X_python-interpreter целиком дорого. Итак, вместо того, чтобы хранить сам data напрямую, почему бы не сохранить какой-то metadata косвенно, то есть the logic how the data is computed.

Есть два подхода к заключению 9X_python таких метаданных.

  1. При объектно-ориентированном подходе мы оборачиваем метаданные as a class. Это так называемый iterator, который реализует протокол итератора (то есть методы __next__() и __iter__()). Это также часто встречающийся iterator design pattern.
  2. Функциональный подход, мы оборачиваем метаданные as a function. Это так называемый generator function. Но под капотом возвращенный generator object по-прежнему IS-A итератор, потому что он также реализует протокол итератора.

В любом случае создается 9X_python-shell итератор, то есть некий объект, который 9X_yield-keyword может предоставить вам нужные данные. Объектно-ориентированный 9X_iterator подход может быть немного сложным. В любом 9X_python-interpreter случае, какой использовать - решать вам.

72
0

Ответ #26

Ответ на вопрос: Что делает ключевое слово "yield"?

Таким образом, оператор yield преобразует вашу 9X_python-interpreter функцию в фабрику, которая создает специальный 9X_iterator объект, называемый generator, который обтекает тело 9X_listiterator вашей исходной функции. Когда generator повторяется, он 9X_listiterator выполняет вашу функцию до тех пор, пока 9X_python-interpreter не достигнет следующего yield, затем приостанавливает 9X_yield-keyword выполнение и оценивает значение, переданное 9X_pythonista в yield. Он повторяет этот процесс на каждой 9X_pythonista итерации, пока путь выполнения не выйдет 9X_iterators из функции. Например,

def simple_generator():
    yield 'one'
    yield 'two'
    yield 'three'

for i in simple_generator():
    print i

просто выводит

one
two
three

Мощность 9X_python-interpreter исходит от использования генератора с циклом, который 9X_python-shell вычисляет последовательность, генератор 9X_python выполняет цикл, останавливаясь каждый раз, чтобы 9X_python-shell «выдать» следующий результат вычисления, таким 9X_coroutine образом он вычисляет список на лету, преимущество 9X_iterators память, сэкономленная для особо больших 9X_iterators вычислений

Допустим, вы хотите создать свою 9X_iterator собственную функцию range, которая генерирует 9X_yield повторяемый диапазон чисел, вы можете сделать 9X_generator это так,

def myRangeNaive(i):
    n = 0
    range = []
    while n < i:
        range.append(n)
        n = n + 1
    return range

и используйте это так;

for i in myRangeNaive(10):
    print i

Но это неэффективно, потому 9X_listiterator что

  • Вы создаете массив, который используете только один раз (это расходует память).
  • Этот код фактически дважды перебирает этот массив! :(

К счастью, Гвидо и его команда были достаточно 9X_pythonista щедры, чтобы разработать генераторы, так 9X_yield что мы могли просто сделать это;

def myRangeSmart(i):
    n = 0
    while n < i:
       yield n
       n = n + 1
    return

for i in myRangeSmart(10):
    print i

Теперь на 9X_generators каждой итерации функция в генераторе с именем 9X_iterator next() выполняет функцию до тех пор, пока не достигнет 9X_python-interpreter оператора yield, в котором она останавливается 9X_yield и «возвращает» значение, или до конца функции. В 9X_listiterator этом случае при первом вызове next() выполняется 9X_yield до оператора yield и yield 'n', при следующем 9X_listiterator вызове он выполнит оператор приращения, вернется 9X_python-shell к 'while', оценит его, и если истина, он 9X_generators остановится и снова выдаст 'n', так будет 9X_python-interpreter продолжаться до тех пор, пока условие while 9X_listiterator не вернет false и генератор не перейдет 9X_yield-keyword к концу функции.

70
0

Ответ #27

Ответ на вопрос: Что делает ключевое слово "yield"?

Доходность - это объект

return в функции возвращает одно значение.

Если 9X_listiterator вы хотите, чтобы функция возвращала огромный набор значений, используйте yield.

Что еще 9X_pythonista более важно, yield - это барьер.

как барьер на языке 9X_py CUDA, он не передает управление, пока не 9X_python-interpreter получит завершено.

То есть он будет запускать 9X_iterators код в вашей функции с самого начала, пока 9X_python-shell не достигнет yield. Затем он вернет первое значение 9X_python-shell цикла.

Затем каждый второй вызов будет запускать 9X_pythonic цикл, который вы написали в функции, еще 9X_coroutine раз, возвращая следующее значение до тех 9X_generators пор, пока не останется никакого значения 9X_iterator для возврата.

66
0

Ответ #28

Ответ на вопрос: Что делает ключевое слово "yield"?

Многие люди используют return, а не yield, но в некоторых 9X_generators случаях yield может быть более эффективным и 9X_iterators с ним проще работать.

Вот пример, для которого 9X_iterator yield определенно лучше всего:

return (в функции)

import random

def return_dates():
    dates = [] # With 'return' you need to create a list then return it
    for i in range(5):
        date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
        dates.append(date)
    return dates

доходность9X_pythonista работе)

def yield_dates():
    for i in range(5):
        date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
        yield date # 'yield' makes a generator automatically which works
                   # in a similar way. This is much more efficient.

Вызов функций

dates_list = return_dates()
print(dates_list)
for i in dates_list:
    print(i)

dates_generator = yield_dates()
print(dates_generator)
for i in dates_generator:
    print(i)

Обе функции делают одно и то же, но 9X_python yield использует три строки вместо пяти и имеет 9X_py на одну переменную меньше, о чем нужно беспокоиться.

Это результат использования кода:

9X_Что делает ключевое слово "yield"?_yield

Как 9X_python видите, обе функции делают одно и то же. Единственная 9X_python разница в том, что return_dates() предоставляет список, а 9X_python yield_dates() - генератор.

Пример из реальной жизни - это 9X_iterators что-то вроде чтения файла построчно или 9X_yield если вы просто хотите создать генератор.

64
0

Ответ #29

Ответ на вопрос: Что делает ключевое слово "yield"?

Ключевое слово yield просто собирает возвращаемые 9X_py результаты. Думайте о yield как о return +=

55
0

Ответ #30

Ответ на вопрос: Что делает ключевое слово "yield"?

yield похож на возвращаемый элемент для функции. Разница 9X_listiterator в том, что элемент yield превращает функцию в 9X_pythonista генератор. Генератор ведет себя точно так 9X_yield-keyword же, как функция, пока что-то не «уступит». Генератор 9X_coroutine останавливается до следующего вызова и продолжает 9X_pythonista работу с той же точки, с которой он был 9X_iterator запущен. Вы можете получить последовательность 9X_listiterator всех "полученных" значений в одном, вызвав 9X_yield-keyword list(generator()).

51
0

Ответ #31

Ответ на вопрос: Что делает ключевое слово "yield"?

Еще один TL; DR

Итератор в списке: next() возвращает следующий элемент 9X_py списка

Генератор итератора: next() будет вычислять следующий элемент 9X_pythonic на лету (выполнить код)

Вы можете увидеть 9X_python-shell yield / generator как способ вручную запустить 9X_iterators поток управления извне (например, продолжить цикл на один 9X_iterator шаг), вызвав next, каким бы сложным ни был поток.

Примечание. Генератор 9X_iterator НЕ является нормальной функцией. Он запоминает 9X_python предыдущее состояние как локальные переменные 9X_iterator (стек). См. Другие ответы или статьи для 9X_listiterator подробного объяснения. Генератор можно повторить только один раз. Вы 9X_yield-keyword могли бы обойтись без yield, но это было бы не 9X_coroutine так хорошо, поэтому его можно считать «очень 9X_iterator приятным» языковым сахаром.

50
0

Ответ #32

Ответ на вопрос: Что делает ключевое слово "yield"?

Вот простой подход, основанный на yield, для 9X_yield вычисления ряда Фибоначчи, объясненный:

def fib(limit=50):
    a, b = 0, 1
    for i in range(limit):
       yield b
       a, b = b, a+b

Когда 9X_yield-keyword вы вводите это в свой REPL, а затем пытаетесь 9X_iterators вызвать его, вы получите загадочный результат:

>>> fib()

Это 9X_python-interpreter связано с тем, что наличие yield сигнализирует 9X_yield Python о том, что вы хотите создать генератор, то 9X_listiterator есть объект, который генерирует значения 9X_yield по запросу.

Итак, как вы генерируете эти 9X_iterators значения? Это можно сделать либо напрямую, используя 9X_yield-keyword встроенную функцию next, либо косвенно, передав 9X_pythonic ее в конструкцию, которая потребляет значения.

Используя 9X_generators встроенную функцию next(), вы напрямую вызываете 9X_iterator .next / __next__, заставляя генератор генерировать значение:

>>> g = fib()
>>> next(g)
1
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
5

Косвенно, если 9X_iterator вы предоставите fib циклу for, инициализатору 9X_yield list, инициализатору tuple или чему-либо еще, ожидающему 9X_yield объект, который генерирует / производит 9X_pythonista значения, вы "потребляете" генератор до 9X_python тех пор, пока он не перестанет генерировать 9X_iterator значения (и он не вернется):

results = []
for i in fib(30):       # consumes fib
    results.append(i) 
# can also be accomplished with
results = list(fib(30)) # consumes fib

Аналогично с 9X_yield-keyword инициализатором tuple:

>>> tuple(fib(5))       # consumes fib
(1, 1, 2, 3, 5)

Генератор отличается от 9X_python-shell функции тем, что он ленив. Это достигается 9X_yield-keyword за счет сохранения своего локального состояния 9X_yield-keyword и возможности возобновления работы в любое 9X_yield время.

Когда вы впервые вызываете fib, вызывая 9X_pythonista его:

f = fib()

Python компилирует функцию, встречает 9X_python-interpreter ключевое слово yield и просто возвращает вам 9X_python-interpreter объект-генератор. Кажется, не очень полезно.

Когда 9X_generator вы затем запрашиваете, он генерирует первое 9X_python-shell значение, прямо или косвенно, он выполняет 9X_iterator все найденные операторы, пока не встречает 9X_pythonic yield, затем возвращает значение, которое вы 9X_yield указали в yield, и приостанавливает работу. В 9X_iterators качестве примера, который лучше демонстрирует 9X_py это, давайте воспользуемся некоторыми вызовами 9X_python-interpreter print (замените на print "text", если на Python 2):

def yielder(value):
    """ This is an infinite generator. Only use next on it """ 
    while 1:
        print("I'm going to generate the value for you")
        print("Then I'll pause for a while")
        yield value
        print("Let's go through it again.")

Теперь 9X_coroutine введите REPL:

>>> gen = yielder("Hello, yield!")

у вас есть объект-генератор, ожидающий 9X_yield-keyword команды для генерации значения. Используйте 9X_yield-keyword next и посмотрите, что будет напечатано:

>>> next(gen) # runs until it finds a yield
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

Результаты 9X_generators без кавычек - это то, что печатается. Цитируемый 9X_generator результат - это то, что возвращается из 9X_pythonista yield. Позвоните next еще раз сейчас:

>>> next(gen) # continues from yield and runs again
Let's go through it again.
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

Генератор запоминает, что 9X_python он был приостановлен на yield value, и возобновляет 9X_coroutine работу с этого момента. Следующее сообщение 9X_pythonista печатается, и снова выполняется поиск оператора 9X_iterators yield, чтобы приостановить его (из-за цикла while).

46
0

Ответ #33

Ответ на вопрос: Что делает ключевое слово "yield"?

yield аналогичен доходу. Разница в следующем:

yield делает 9X_iterators функцию итерируемой (в следующем примере 9X_generators функция primes(n = 1) становится итерируемой).
По сути, это 9X_generators означает, что при следующем вызове функции 9X_pythonista она продолжится с того места, где была оставлена 9X_yield ​​(то есть после строки yield expression).

def isprime(n):
    if n == 1:
        return False
    for x in range(2, n):
        if n % x == 0:
            return False
    else:
        return True

def primes(n = 1):
   while(True):
       if isprime(n): yield n
       n += 1 

for n in primes():
    if n > 100: break
    print(n)

В приведенном 9X_yield выше примере, если isprime(n) истинно, будет возвращено 9X_python-shell простое число. В следующей итерации он будет 9X_iterators продолжен со следующей строки

n += 1  

41
0

Ответ #34

Ответ на вопрос: Что делает ключевое слово "yield"?

В Python generators (особый тип iterators) используются для 9X_python-shell генерации серий значений, а ключевое слово 9X_python-shell yield аналогично ключевому слову return функций генератора.

Еще одна интересная особенность ключевого слова yield - это сохранение state функции генератора.

Итак, мы 9X_iterator можем устанавливать для number другое значение 9X_python-interpreter каждый раз, когда generator дает результат.

Вот пример:

def getPrimes(number):
    while True:
        if isPrime(number):
            number = yield number     # a miracle occurs here
        number += 1

def printSuccessivePrimes(iterations, base=10):
    primeGenerator = getPrimes(base)
    primeGenerator.send(None)
    for power in range(iterations):
        print(primeGenerator.send(base ** power))

32
0

Ответ #35

Ответ на вопрос: Что делает ключевое слово "yield"?

yield что-то дает. Как будто кто-то просит вас 9X_python-interpreter сделать 5 кексов. Если вы закончили хотя 9X_yield бы с одним кексом, вы можете дать им поесть, пока 9X_python-interpreter готовите другие пирожные.

In [4]: def make_cake(numbers):
   ...:     for i in range(numbers):
   ...:         yield 'Cake {}'.format(i)
   ...:

In [5]: factory = make_cake(5)

Здесь factory называется 9X_py генератором, который делает вам пирожные. Если 9X_py вы вызовете make_function, вы получите генератор вместо 9X_generators запуска этой функции. Это потому, что когда 9X_iterator ключевое слово yield присутствует в функции, оно 9X_python-interpreter становится генератором.

In [7]: next(factory)
Out[7]: 'Cake 0'

In [8]: next(factory)
Out[8]: 'Cake 1'

In [9]: next(factory)
Out[9]: 'Cake 2'

In [10]: next(factory)
Out[10]: 'Cake 3'

In [11]: next(factory)
Out[11]: 'Cake 4'

Они съели все пирожные, но 9X_python-shell просят еще один.

In [12]: next(factory)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
 in 
----> 1 next(factory)

StopIteration:

и им говорят перестать спрашивать 9X_yield больше. Итак, как только вы съели генератор, вы 9X_generators закончите с ним. Вам нужно снова вызвать 9X_generator make_cake, если вы хотите больше тортов. Это все 9X_pythonista равно, что разместить очередной заказ на 9X_pythonista кексы.

In [13]: factory = make_cake(3)

In [14]: for cake in factory:
    ...:     print(cake)
    ...:
Cake 0
Cake 1
Cake 2

Вы также можете использовать цикл 9X_generators for с генератором, подобным приведенному 9X_yield-keyword выше.

Еще один пример: допустим, вам нужен 9X_yield-keyword случайный пароль, когда вы его запрашиваете.

In [22]: import random

In [23]: import string

In [24]: def random_password_generator():
    ...:     while True:
    ...:         yield ''.join([random.choice(string.ascii_letters) for _ in range(8)])
    ...:

In [25]: rpg = random_password_generator()

In [26]: for i in range(3):
    ...:     print(next(rpg))
    ...:
FXpUBhhH
DdUDHoHn
dvtebEqG

In [27]: next(rpg)
Out[27]: 'mJbYRMNo'

Здесь 9X_yield rpg - генератор, который может генерировать 9X_listiterator бесконечное количество случайных паролей. Таким 9X_coroutine образом, мы также можем сказать, что генераторы 9X_py полезны, когда мы не знаем длины последовательности, в 9X_yield-keyword отличие от списка, который имеет конечное 9X_pythonista число элементов.

31
0

Ответ #36

Ответ на вопрос: Что делает ключевое слово "yield"?

Все ответы здесь прекрасны; но только один 9X_iterators из них (получивший наибольшее количество 9X_py голосов) касается того, как работает ваш код. Другие относятся к генераторам в 9X_python-interpreter целом и к тому, как они работают.

Поэтому 9X_yield я не буду повторять, что такое генераторы 9X_python-interpreter и что делают урожаи; Я думаю, что на них 9X_generators есть отличные существующие ответы. Однако, потратив 9X_generators несколько часов на попытки понять код, похожий 9X_generators на ваш, я расскажу, как он работает.

Ваш 9X_pythonista код проходит через двоичную древовидную 9X_pythonic структуру. Возьмем, к примеру, это дерево:

    5
   / \
  3   6
 / \   \
1   4   8

И 9X_python еще одна более простая реализация обхода 9X_yield дерева двоичного поиска:

class Node(object):
..
def __iter__(self):
    if self.has_left_child():
        for child in self.left:
            yield child

    yield self.val

    if self.has_right_child():
        for child in self.right:
            yield child

Код выполнения находится 9X_generator в объекте Tree, который реализует __iter__ следующим 9X_generator образом:

def __iter__(self):

    class EmptyIter():
        def next(self):
            raise StopIteration

    if self.root:
        return self.root.__iter__()
    return EmptyIter()

Оператор while candidates можно заменить на for element in tree; Python 9X_listiterator переведет это на

it = iter(TreeObj)  # returns iter(self.root) which calls self.root.__iter__()
for element in it: 
    .. process element .. 

Поскольку функция Node.__iter__ является 9X_iterators генератором, код внутри нее выполняется на каждой 9X_yield итерации. Таким образом, казнь будет выглядеть 9X_yield так:

  1. корневой элемент является первым; проверьте, остались ли дочерние элементы, и for выполнит итерацию по ним (назовем его it1, потому что это первый объект итератора)
  2. у него есть дочерний элемент, поэтому выполняется for. for child in self.left создает новый итератор из self.left, который сам является объектом узла (it2)
  3. Та же логика, что и 2, но создается новый iterator (it3)
  4. Теперь мы достигли левого конца дерева. it3 не имеет левых дочерних элементов, поэтому он продолжается и yield self.value
  5. При следующем вызове next(it3) он вызывает StopIteration и существует, поскольку у него нет правильных дочерних элементов (он достигает конца функции, ничего не возвращая).
  6. it1 и it2 все еще активны - они не исчерпаны, и вызов next(it2) приведет к получению значений, а не к увеличению StopIteration
  7. Теперь мы вернулись к контексту it2 и вызываем next(it2), который продолжается с того места, где он остановился: сразу после оператора yield child. Поскольку у него больше нет левых дочерних элементов, он продолжает работу и возвращает его self.val.

Уловка здесь в том, что каждая итерация 9X_iterators создает под-итераторы для обхода дерева и сохраняет состояние 9X_pythonic текущего итератора. Достигнув конца, он 9X_yield возвращается по стеку, и значения возвращаются 9X_generators в правильном порядке (сначала возвращается 9X_python-interpreter наименьшее значение).

В вашем примере кода 9X_python-shell было сделано нечто похожее с использованием 9X_yield другой техники: он заполнил одноэлементный список для каждого 9X_python-interpreter дочернего элемента, затем на следующей итерации 9X_pythonic он вытащил его и запустил код функции для 9X_pythonic текущего объекта ( отсюда self).

Надеюсь, это 9X_generator немного повлияло на эту легендарную тему. Я 9X_generator потратил несколько часов на рисование этого 9X_generator процесса, чтобы понять его.

23
0

Ответ #37

Ответ на вопрос: Что делает ключевое слово "yield"?

ключевое слово yield в python используется 9X_py для выхода из кода без нарушения состояния 9X_pythonic локальной переменной и при повторном вызове 9X_python-shell функции выполнение начинается с последней 9X_yield точки, где мы оставили код.

Пример ниже демонстрирует 9X_iterator его работу:

def counter():
    x=2
    while x < 5:
        yield x
        x += 1
        
print("Initial value of x: ", counter()) 

x=2
x=x+1

for y in counter():
    print(y)

Приведенный выше код генерирует 9X_generators следующий вывод:

Начальное значение x: <счетчик 9X_yield объектов генератора по адресу 0x7f0263020ac0>

2

3

4

17
1

  • @ pippo1980 это просто для того, чтобы показать, что на `x` ...

Ответ #38

Ответ на вопрос: Что делает ключевое слово "yield"?

Может также отправлять данные обратно в генератор!

Действительно, как объясняется здесь во 9X_yield многих ответах, использование yield создает generator.

Вы 9X_python-shell можете использовать ключевое слово yield для 9X_coroutine отправки данных обратно в "живой" генератор.

Пример:

Допустим, у нас есть метод, который переводит 9X_pythonic с английского на какой-то другой язык. И 9X_iterator вначале он делает что-то тяжелое, и это 9X_pythonic нужно сделать один раз. Мы хотим, чтобы 9X_pythonista этот метод работал вечно (не знаю почему 9X_python-shell .. :)) и получал слова слова для перевода.

def translator():
    # load all the words in English language and the translation to 'other lang'
    my_words_dict = {'hello': 'hello in other language', 'dog': 'dog in other language'}

    while True:
        word = (yield)
        yield my_words_dict.get(word, 'Unknown word...')

Бег:

my_words_translator = translator()

next(my_words_translator)
print(my_words_translator.send('dog'))

next(my_words_translator)
print(my_words_translator.send('cat'))

напечатает:

dog in other language
Unknown word...

Подведем итоги:

используйте 9X_coroutine метод send внутри генератора для отправки данных 9X_yield обратно в генератор. Для этого используется 9X_python (yield).

16
0

Ответ #39

Ответ на вопрос: Что делает ключевое слово "yield"?

yield в python похож на оператор return, за 9X_iterator исключением некоторых отличий. Если из функции 9X_generators должно быть возвращено несколько значений, оператор 9X_pythonic return вернет все значения в виде списка, и 9X_listiterator он должен быть сохранен в памяти в блоке 9X_pythonic вызывающего. Но что, если мы не хотим использовать 9X_listiterator дополнительную память? Вместо этого мы хотим 9X_iterator получить значение от функции, когда оно 9X_python-shell нам нужно. Вот где приходит доходность. Рассмотрим 9X_generators следующую функцию: -

def fun():
   yield 1
   yield 2
   yield 3

И вызывающий абонент: -

def caller():
   print ('First value printing')
   print (fun())
   print ('Second value printing')
   print (fun())
   print ('Third value printing')
   print (fun())

Приведенный 9X_yield выше сегмент кода (вызывающая функция) при 9X_py вызове выводит: -

First value printing
1
Second value printing
2
Third value printing
3

Как видно из вышеизложенного, yield 9X_pythonic возвращает значение вызывающей стороне, но 9X_listiterator когда функция вызывается снова, она начинается 9X_iterators не с первого оператора, а с оператора сразу 9X_py после yield. В приведенном выше примере 9X_iterators была напечатана «Печать первого значения» и 9X_generator была вызвана функция. 1 был возвращен и 9X_yield напечатан. Затем была напечатана «печать 9X_python-interpreter второго значения» и снова был вызван fun(). Вместо 9X_coroutine того, чтобы печатать 1 (первый оператор), он 9X_yield-keyword вернул 2, т.е. оператор сразу после yield 9X_generators 1. Тот же процесс повторяется и дальше.

13
0

Ответ #40

Ответ на вопрос: Что делает ключевое слово "yield"?

Простыми словами

Оператор yield приостанавливает выполнение 9X_generator функции и отправляет значение обратно вызывающей 9X_coroutine стороне, но сохраняет состояние, достаточное 9X_generators для возобновления функции с того места, где 9X_py она была остановлена. При возобновлении 9X_generator функция продолжает выполнение сразу после 9X_pythonista последнего выполнения yield. Это позволяет 9X_yield его коду создавать серию значений с течением 9X_listiterator времени, а не вычислять их сразу и отправлять 9X_generator обратно в виде списка.

Давайте посмотрим на примере:

# A Simple Python program to demonstrate working
# of yield
  
# A generator function that yields 1 for the first time,
# 2 second time and 3 third time
def simpleGeneratorFun():
    yield 1
    yield 2
    yield 3
  

Код драйвера для проверки вышеуказанной функции генератора

for value in simpleGeneratorFun(): 
    print(value)

Output:

1
2
3

Return отправляет 9X_coroutine указанное значение обратно вызывающей стороне, тогда 9X_yield-keyword как Yield может генерировать последовательность 9X_pythonista значений. Нам следует использовать yield, когда 9X_py мы хотим перебрать последовательность, но 9X_coroutine не хотим сохранять всю последовательность 9X_yield в памяти.

Доходность используются в генераторах Python. Функция 9X_python генератора определяется как обычная функция, но 9X_pythonic всякий раз, когда ей нужно сгенерировать 9X_yield значение, она делает это с ключевым словом 9X_generator yield, а не return. Если тело def содержит 9X_listiterator yield, функция автоматически становится 9X_pythonic функцией-генератором.

11
0

Ответ #41

Ответ на вопрос: Что делает ключевое слово "yield"?

Простой ответ

Когда функция содержит хотя бы один оператор 9X_iterator yield, функция автоматически становится функцией 9X_python-shell генератора. Когда вы вызываете функцию генератора, python 9X_pythonic выполняет код в функции генератора до тех 9X_generator пор, пока не появится оператор yield. Оператор 9X_generator yield замораживает функцию со всеми ее внутренними 9X_yield состояниями. Когда вы снова вызываете функцию 9X_generators генератора, python продолжает выполнение 9X_yield кода в функции генератора из замороженного 9X_generators положения, пока оператор yield не будет повторяться 9X_coroutine снова и снова. Функция генератора выполняет 9X_yield код до тех пор, пока функция генератора 9X_yield не завершится без оператора yield.

Тест

Создайте список 9X_coroutine и верните его:

def my_range(n):
    my_list = []
    i = 0
    while i < n:
        my_list.append(i)
        i += 1
    return my_list

@profile
def function():
    my_sum = 0
    my_values = my_range(1000000)
    for my_value in my_values:
        my_sum += my_value

function()

Результаты с:

Total time: 1.07901 s
Timer unit: 1e-06 s

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     9                                           @profile
    10                                           def function():
    11         1          1.1      1.1      0.0      my_sum = 0
    12         1     494875.0 494875.0     45.9      my_values = my_range(1000000)
    13   1000001     262842.1      0.3     24.4      for my_value in my_values:
    14   1000000     321289.8      0.3     29.8          my_sum += my_value



Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
     9   40.168 MiB   40.168 MiB           1   @profile
    10                                         def function():
    11   40.168 MiB    0.000 MiB           1       my_sum = 0
    12   78.914 MiB   38.746 MiB           1       my_values = my_range(1000000)
    13   78.941 MiB    0.012 MiB     1000001       for my_value in my_values:
    14   78.941 MiB    0.016 MiB     1000000           my_sum += my_value

Генерация значений 9X_listiterator на лету:

def my_range(n):
    i = 0
    while i < n:
        yield i
        i += 1

@profile
def function():
    my_sum = 0
    
    for my_value in my_range(1000000):
        my_sum += my_value

function()

Результаты с:

Total time: 1.24841 s
Timer unit: 1e-06 s

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     7                                           @profile
     8                                           def function():
     9         1          1.1      1.1      0.0      my_sum = 0
    10
    11   1000001     895617.3      0.9     71.7      for my_value in my_range(1000000):
    12   1000000     352793.7      0.4     28.3          my_sum += my_value



Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
     7   40.168 MiB   40.168 MiB           1   @profile
     8                                         def function():
     9   40.168 MiB    0.000 MiB           1       my_sum = 0
    10
    11   40.203 MiB    0.016 MiB     1000001       for my_value in my_range(1000000):
    12   40.203 MiB    0.020 MiB     1000000           my_sum += my_value

Резюме

Для выполнения функции 9X_yield генератора требуется немного больше времени, чем 9X_python-shell функции, которая возвращает список, но использует 9X_python-shell гораздо меньше памяти.

8
0

Ответ #42

Ответ на вопрос: Что делает ключевое слово "yield"?

Простой пример использования:

>>> def foo():
    yield 100
    yield 20
    yield 3

    
>>> for i in foo(): print(i)

100
20
3
>>> 

Как это работает: при 9X_iterators вызове функция немедленно возвращает объект. Объект 9X_coroutine можно передать в функцию next(). Всякий 9X_generator раз, когда вызывается функция next(), ваша 9X_generator функция выполняется до следующего yield 9X_python и предоставляет возвращаемое значение для 9X_generator функции next().

Под капотом цикл for распознает, что 9X_pythonista объект является объектом-генератором, и 9X_listiterator использует next() для получения следующего 9X_yield значения.

В некоторых языках, таких как ES6 9X_python-interpreter и выше, это реализовано немного по-другому, поэтому 9X_iterator next является функцией-членом объекта-генератора, и 9X_yield вы можете передавать значения от вызывающего 9X_python-interpreter каждый раз, когда он получает следующее 9X_pythonic значение. Итак, если результат является 9X_generators генератором, вы можете сделать что-то вроде 9X_yield-keyword y = result.next (555), а программа, выдающая 9X_yield значения, могла бы сказать что-то вроде 9X_yield z = yield 999. Значение y будет 999, которое 9X_iterator затем будет получено из yield, и значение 9X_generators z будет 555, что дает доход от следующего. Python, похоже, этого 9X_coroutine не делает (пока? Может, когда-нибудь?)

8
0

Ответ #43

Ответ на вопрос: Что делает ключевое слово "yield"?

Обычно он используется для создания итератора 9X_coroutine вне функции. Подумайте, что «yield» - это 9X_generators добавление() к вашей функции, а ваша функция 9X_listiterator - как массив. И если определенные критерии 9X_generators соответствуют, вы можете добавить это значение 9X_yield-keyword в свою функцию, чтобы сделать ее итератором.

arr=[]
if 2>0:
   arr.append(2)

def func():
   if 2>0:
      yield 2

результат 9X_pythonista будет одинаковым для обоих.

Основное преимущество 9X_pythonista использования yield - создание итераторов. Итераторы 9X_python-interpreter не вычисляют значение каждого элемента при 9X_iterator создании экземпляра. Они вычисляют это только 9X_yield-keyword тогда, когда вы об этом просите. Это называется 9X_python ленивым вычислением.

6
0

Ответ #44

Ответ на вопрос: Что делает ключевое слово "yield"?

Функция - возвращает.

Генератор - доходность 9X_listiterator (содержит одну или несколько доходностей 9X_py и ноль или несколько доходностей).

names = ['Sam', 'Sarah', 'Thomas', 'James']


# Using function
def greet(name) :
    return f'Hi, my name is {name}.'
    
for each_name in names:
    print(greet(each_name))

# Output:   
>>>Hi, my name is Sam.
>>>Hi, my name is Sarah.
>>>Hi, my name is Thomas.
>>>Hi, my name is James.


# using generator
def greetings(names) :
    for each_name in names:
        yield f'Hi, my name is {each_name}.'
 
for greet_name in greetings(names):
    print (greet_name)

# Output:    
>>>Hi, my name is Sam.
>>>Hi, my name is Sarah.
>>>Hi, my name is Thomas.
>>>Hi, my name is James.

Генератор 9X_yield выглядит как функция, но ведет себя как 9X_pythonic итератор.

Генератор продолжает выполнение 9X_python-interpreter с того места, где он был прекращен (или 9X_pythonista передан). При возобновлении функция продолжает 9X_py выполнение сразу после последнего прогона 9X_pythonic yield. Это позволяет его коду создавать 9X_generators серию значений с течением времени, а не 9X_coroutine вычислять их все сразу и отправлять обратно 9X_pythonista в виде списка.

def function():
    yield 1 # return this first
    yield 2 # start continue from here (yield don't execute above code once executed)
    yield 3 # give this at last (yield don't execute above code once executed)

for processed_data in function(): 
    print(processed_data)
    
#Output:

>>>1
>>>2
>>>3

Примечание. Выход не должен 9X_generator входить в конструкцию try ... finally.

4
0

Ответ #45

Ответ на вопрос: Что делает ключевое слово "yield"?

Ключевое слово yield используется в перечислении/итерации, когда 9X_python ожидается, что функция вернет более одного 9X_yield вывода. Я хочу привести этот очень простой 9X_coroutine пример:

def getNumber():
    for r in range(1,10):
        return r

Приведенная выше функция вернет только 9X_generators 1, даже если она вызывается несколько раз. Теперь, если 9X_yield мы заменим return на yield как:

def getNumber():
    for r in range(1,10):
        yield r

Он вернет 1 при первом 9X_pythonic вызове 2 при повторном вызове, затем 3, 4 и 9X_python будет увеличиваться до 10.

4
0

Ответ #46

Ответ на вопрос: Что делает ключевое слово "yield"?

Генераторы позволяют сразу получать отдельные 9X_generators обработанные элементы (без необходимости 9X_generator ждать обработки всей коллекции). Это показано 9X_pythonista в примере ниже.

import time

def get_gen():
    for i in range(10):
        yield i
        time.sleep(1)

def get_list():
    ret = []
    for i in range(10):
        ret.append(i)
        time.sleep(1)
    return ret


start_time = time.time()
print('get_gen iteration (individual results come immediately)')
for i in get_gen():
    print(f'result arrived after: {time.time() - start_time:.0f} seconds')
print()

start_time = time.time()
print('get_list iteration (results come all at once)') 
for i in get_list():
    print(f'result arrived after: {time.time() - start_time:.0f} seconds')

get_gen iteration (individual results come immediately)
result arrived after: 0 seconds
result arrived after: 1 seconds
result arrived after: 2 seconds
result arrived after: 3 seconds
result arrived after: 4 seconds
result arrived after: 5 seconds
result arrived after: 6 seconds
result arrived after: 7 seconds
result arrived after: 8 seconds
result arrived after: 9 seconds

get_list iteration (results come all at once)
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds

4
0

Ответ #47

Ответ на вопрос: Что делает ключевое слово "yield"?

Ключевые моменты
  • grammar for Python использует наличие ключевого слова yield для 9X_yield-keyword создания функции, которая возвращает generator.

  • Генератор 9X_listiterator — это своего рода iterator, который является основным 9X_python-shell способом создания цикла в Python.

  • Генератор 9X_iterator — это, по существу, возобновляемая функция. В 9X_pythonic отличие от return, которое возвращает значение 9X_python-shell и завершает функцию, ключевое слово yield возвращает 9X_pythonista значение и приостанавливает функцию.

  • Когда 9X_python-shell next(g) вызывается для генератора, функция возобновляет 9X_yield-keyword выполнение с того места, где оно было остановлено.

  • Только 9X_iterators когда функция сталкивается с явным или подразумеваемым 9X_yield-keyword return, она фактически завершается.

Техника написания и понимания генераторов

Простой способ 9X_python-shell понять генераторы и подумать о них — написать 9X_coroutine обычную функцию с print() вместо yield:

def f(n):
    for x in range(n):
        print(x)
        print(x * 10)

Смотрите, что 9X_iterator выдает:

>>> f(3)
0
0
1
10
2
2

Когда эта функция понятна, замените 9X_pythonista yield на print, чтобы получить генератор, который 9X_yield-keyword выдает те же значения:

def f(n):
    for x in range(n):
        yield x
        yield x * 10

Что дает:

>>> list(f(3))
[0, 0, 1, 10, 2, 20]

Протокол итератора

Ответ на 9X_iterator вопрос "что делает yield" может 9X_pythonista быть коротким и простым, но он является 9X_python частью большого мира, так называемого "итераторного 9X_pythonic протокола".

На стороне отправителя протокола 9X_pythonista итератора есть два соответствующих типа 9X_python-interpreter объектов. iterables — это то, что вы можете перебирать. А 9X_generators итераторы — это объекты, отслеживающие состояние 9X_yield цикла.

На стороне потребителя протокола итератора 9X_listiterator мы вызываем iter() для итерируемого объекта, чтобы 9X_generators получить итератор. Затем мы вызываем next() на 9X_iterators итераторе, чтобы получить значения из итератора. Когда 9X_py данных больше нет, возникает исключение 9X_python-interpreter StopIteration:

>>> s = [10, 20, 30]    # The list is the "iterable"
>>> it = iter(s)        # This is the "iterator"
>>> next(it)            # Gets values out of an iterator
10
>>> next(it)
20
>>> next(it)
30
>>> next(it)
Traceback (most recent call last):
 ...
StopIteration

Чтобы упростить нам задачу, циклы for вызывают 9X_python-shell iter и next от нашего имени:

>>> for x in s:
...     print(x)
...   
10
20
30

Обо всем этом 9X_generators можно написать книгу, но это ключевые моменты. Когда 9X_listiterator я преподаю курсы Python, я обнаружил, что 9X_iterator это минимальное объяснение, достаточное 9X_python-interpreter для понимания и немедленного начала его 9X_generator использования. В частности, способ написать 9X_yield-keyword функцию с print, протестировать ее и затем преобразовать 9X_python в yield, похоже, хорошо работает с программистами 9X_iterators Python любого уровня.

3
0

Ответ #48

Ответ на вопрос: Что делает ключевое слово "yield"?

Чтобы понять его функцию доходности, нужно 9X_yield-keyword понять, что такое генератор. Более того, прежде 9X_python-shell чем разбираться в генераторах, вы должны 9X_generators понять iterables. Повторяемый: повторяемый Чтобы 9X_python создать список, вам, естественно, нужно 9X_python иметь возможность читать каждый элемент 9X_python-interpreter по одному. Процесс чтения его элементов 9X_pythonista один за другим называется итерацией:

>>> mylist = [1, 2, 3]
>>> for i in mylist:
...    print(i)
1
2
3 

mylist 9X_yield является итерируемым. Когда вы используете 9X_pythonista списки, вы создаете список и, следовательно, повторяемый:

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4 

Все 9X_listiterator структуры данных, которые можно использовать 9X_python-interpreter для... в..., являются итерируемыми; списки, строки, файлы...

Эти 9X_pythonista итерируемые методы удобны тем, что их можно 9X_listiterator читать по желанию, но вы храните все значения 9X_python-interpreter в памяти, что не всегда желательно, когда 9X_yield у вас много значений. Генератор: генератор Генератор 9X_coroutine также является своего рода итератором, особым 9X_python-interpreter видом итерации, которую можно повторить 9X_listiterator только один раз. Генератор не хранит все 9X_py значения в памяти, а генерирует значения 9X_generators на лету:

генератор: генератор, генератор, генератор 9X_python вырабатывает электроэнергию, но не хранит 9X_coroutine энергию ;)

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
0
1
4 

Пока () используется вместо [], понимание 9X_iterator списка становится пониманием генератора. Однако, поскольку 9X_iterators генератор можно использовать только один 9X_listiterator раз, вы не можете выполнить для i в mygenerator 9X_generator второй раз: генератор вычисляет 0, затем 9X_generator отбрасывает его, затем вычисляет 1 и в последний 9X_yield раз вычисляет 4. Типичный черный слепой 9X_listiterator ломает кукурузу .

Ключевое слово yield используется 9X_py так же, как и return, за исключением того, что 9X_listiterator функция возвращает генератор.

>>> def createGenerator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = createGenerator() 
>>> print(mygenerator) 

>>> for i in mygenerator:
...     print(i)
0
1
4 

Сам по себе 9X_pythonista этот пример бесполезен, но когда вам нужна 9X_py функция для возврата большого количества 9X_iterator значений и нужно прочитать ее только один 9X_python-interpreter раз, использование yield становится удобным.

Чтобы 9X_python освоить yield, нужно четко понимать, что 9X_pythonic при вызове функции код, написанный в теле 9X_py функции, не будет выполняться. Функция возвращает 9X_yield-keyword только объект генератора. Новичков это может 9X_yield-keyword смутить.

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

Самое 9X_iterator сложное сейчас это:

При первом вызове объекта-генератора, созданного 9X_python из вашей функции, он будет запускать код 9X_python-shell в функции с самого начала, пока не достигнет 9X_yield-keyword yield, а затем вернет первое значение цикла. Затем 9X_generator каждый последующий вызов будет запускать 9X_coroutine следующую итерацию цикла, который вы написали 9X_python-shell в функции, и возвращать следующее значение. Это 9X_coroutine будет продолжаться до тех пор, пока генератор 9X_generator не будет считаться пустым, что приводит 9X_py к отсутствию попадания во время выполнения 9X_yield-keyword функции. Это может быть из-за того, что 9X_coroutine цикл закончился, или из-за того, что вас 9X_iterators больше не устраивает "if/else".

Личное понимание, я 9X_iterators надеюсь помочь вам!

2
0

Ответ #49

Ответ на вопрос: Что делает ключевое слово "yield"?

yield используется для создания generator. Думайте о генераторе 9X_yield-keyword как об итераторе, который дает вам ценность 9X_iterator на каждой итерации. Когда вы используете 9X_pythonic yield в цикле, вы получаете объект генератора, который 9X_pythonic вы можете использовать для получения элементов 9X_generators из цикла итеративным образом

1
0