Как используется ключевое слово yield
в Python? Что оно делает?
Например, я'пытаюсь понять этот код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
Что происходит при вызове метода _get_child_candidates
?
Возвращается ли список? Один элемент? Вызывается ли он снова? Когда последующие вызовы прекратятся?
Чтобы понять, что делает yield
, вы должны понять, что такое генераторы. А прежде чем понять генераторы, необходимо понять итерабельные таблицы.
Когда вы создаете список, вы можете читать его элементы по одному. Чтение элементов списка по одному называется итерацией:
>>> mylist = [1, 2, 3]
>>> for i in mylist:
... print(i)
1
2
3
mylist
- это итерируемая. Когда вы используете понимание списка, вы создаете список, а значит, и итерабельность:
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
... print(i)
0
1
4
Все, над чем можно использовать "for... in...
", является итерабельным; списки
, строки
, файлы...
Эти итерационные таблицы удобны тем, что вы можете читать их сколько угодно, но вы храните все значения в памяти, а это не всегда то, что вам нужно, когда у вас много значений.
Генераторы - это итераторы, разновидность итерабельной таблицы вы можете выполнять итерацию только один раз. Генераторы не хранят все значения в памяти, они генерируют значения на лету:
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
0
1
4
Это то же самое, только вместо []
вы использовали ()
. НО, вы не можете выполнить for i in mygenerator
во второй раз, так как генераторы могут быть использованы только один раз: они вычисляют 0, затем забывают об этом и вычисляют 1, и в конце вычисляют 4, один за другим.
yield
- это ключевое слово, которое используется подобно return
, за исключением того, что функция возвращает генератор.
>>> def createGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
4
Здесь это бесполезный пример, но он удобен, когда вы знаете, что ваша функция вернет огромный набор значений, которые вам нужно будет прочитать только один раз.
Чтобы освоить yield
, вы должны понять, что когда вы вызываете функцию, код, который вы написали в теле функции, не выполняется. Функция только возвращает объект генератора, это немного хитро :-)
Затем, ваш код будет продолжаться с того места, на котором он остановился каждый раз, когда for
использует генератор.
Теперь самая сложная часть:
В первый раз, когда for
вызывает объект-генератор, созданный из вашей функции, он будет выполнять код в вашей функции с самого начала, пока не нажмет yield
, затем он вернет первое значение цикла. Затем, каждый следующий вызов будет запускать цикл, который вы написали в функции, еще раз и возвращать следующее значение, пока не останется ни одного значения для возврата.
Генератор считается пустым, когда функция выполняется, но больше не нажимает yield
. Это может быть потому, что цикл завершился, или потому, что вы больше не удовлетворяете если/или
.
Генератор:
# 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
Этот код содержит несколько умных частей:
цикл выполняет итерацию по списку, но список расширяется во время итерации цикла :-) Это лаконичный способ перебрать все эти вложенные данные, даже если он немного опасен, так как в итоге вы можете получить бесконечный цикл. В данном случае candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
исчерпывает все значения генератора, но while
продолжает создавать новые объекты генератора, которые будут выдавать значения, отличные от предыдущих, поскольку они не применяются на одном и том же узле.
Метод extend()
- это метод объекта list, который ожидает итерабельную переменную и добавляет ее значения в список.
Обычно мы передаем ему список:
>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]
Но в вашем коде он получает генератор, что хорошо, потому что:
И это работает, потому что Python не волнует, является ли аргумент метода списком или нет. Python ожидает итерации, поэтому он будет работать со строками, списками, кортежами и генераторами! Это называется "утиная типизация" и является одной из причин, почему Python так крут. Но это уже другая история, для другого вопроса...
Вы можете остановиться здесь, или прочитать немного дальше, чтобы увидеть продвинутое использование генератора:
>>> 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())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> 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 используйте print(corner_street_atm.__next__())
или print(next(corner_street_atm))
.
Это может быть полезно для различных вещей, например, для контроля доступа к ресурсу.
Модуль itertools содержит специальные функции для работы с итерациями. Вы когда-нибудь хотели продублировать генератор?
Соединить два генератора в цепочку? Сгруппировать значения во вложенном списке с помощью одной строки? Создать Map / Zip
без создания еще одного списка?
Тогда просто импортируйте itertools
.
Пример? Давайте посмотрим возможные порядки прибытия для скачек четырех лошадей:
>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> 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)]
Итерация - это процесс, подразумевающий итерации (реализующие метод __iter__()
) и итераторы (реализующие метод __next__()
).
Итераторы - это любые объекты, из которых можно получить итератор. Итераторы - это объекты, которые позволяют вам выполнять итерацию по итерациям.
Подробнее об этом в этой статье как работают циклы for
.
yield
Когда вы видите функцию с утверждениями yield
, примените этот простой прием, чтобы понять, что произойдет:
result = []
в начало функции.yield expr
на result.append(expr)
.return result
в нижней части функции.yield
! Прочитайте и разберитесь в коде.Этот трюк может дать вам представление о логике, стоящей за функцией, но то, что на самом деле происходит с yield
, значительно отличается от того, что происходит при подходе на основе списка. Во многих случаях подход yield будет гораздо более эффективным с точки зрения использования памяти и более быстрым. В других случаях этот трюк приведет к тому, что вы застрянете в бесконечном цикле, даже если исходная функция работает нормально. Читайте дальше, чтобы узнать больше...
Во-первых, протокол итераторов - когда вы пишете
for x in mylist:
...loop body...
Python выполняет следующие два действия:
Получает итератор для mylist
:
Вызываем iter(mylist)
-> это возвращает объект с методом next()
(или __next__()
в Python 3).
[Об этом шаге большинство людей забывают рассказать].
Использует итератор для перебора элементов:
Продолжайте вызывать метод next()
на итераторе, возвращенном из шага 1. Возвращаемое значение из next()
присваивается x
и выполняется тело цикла. Если внутри next()
возникает исключение StopIteration
, это означает, что в итераторе больше нет значений и цикл завершается.
На самом деле Python выполняет два вышеупомянутых шага каждый раз, когда хочет просмотреть содержимое объекта - так что это может быть цикл for, но это также может быть код типа otherlist.extend(mylist)
(где otherlist
- это список Python).
Здесь mylist
- это iterable, потому что он реализует протокол итератора. В классе, определенном пользователем, вы можете реализовать метод __iter__()
, чтобы сделать экземпляры вашего класса итерабельными. Этот метод должен возвращать итератор. Итератор - это объект с методом next()
. Можно реализовать и __iter__()
, и next()
в одном классе, и пусть __iter__()
возвращает self
. Это будет работать в простых случаях, но не тогда, когда вам нужно, чтобы два итератора одновременно обходили один и тот же объект.
Таков протокол итератора, многие объекты реализуют этот протокол:
__iter__()
.Обратите внимание, что цикл for
не знает, с каким объектом он имеет дело - он просто следует протоколу итератора, и рад получать элемент за элементом по мере вызова next()
. Встроенные списки возвращают свои элементы по одному, словари возвращают ключи по одному, файлы возвращают строки по одному и т.д. А генераторы возвращают... ну, тут-то и приходит на помощь yield
:
def f123():
yield 1
yield 2
yield 3
for item in f123():
print item
Вместо операторов yield
, если бы в f123()
было три оператора return
, только первый был бы выполнен, и функция завершилась бы. Но f123()
не является обычной функцией. Когда вызывается f123()
, она не возвращает ни одного из значений в операторах yield! Она возвращает объект генератора. Кроме того, функция на самом деле не завершается - она переходит в приостановленное состояние. Когда цикл for
пытается перебрать объект-генератор, функция возобновляет свое приостановленное состояние на следующей строке после yield
, из которой она ранее вернулась, выполняет следующую строку кода, в данном случае оператор yield
, и возвращает его в качестве следующего элемента. Так происходит до выхода функции, в этот момент генератор выдает StopIteration
, и цикл завершается.
Таким образом, объект генератора является своего рода адаптером - на одном конце он демонстрирует протокол итератора, раскрывая методы __iter__()
и next()
, чтобы цикл for
был счастлив. Однако на другом конце он запускает функцию только для того, чтобы получить из нее следующее значение, и переводит ее обратно в приостановленный режим.
Обычно можно написать код, который не использует генераторы, но реализует ту же логику. Один из вариантов - использовать "трюк" с временным списком, о котором я уже говорил. Это будет работать не во всех случаях, например, если у вас есть бесконечные циклы, или это может неэффективно использовать память, если у вас очень длинный список. Другой подход заключается в реализации нового итерируемого класса SomethingIter
, который хранит состояние в членах экземпляра и выполняет следующий логический шаг в методе next()
(или __next__()
в Python 3). В зависимости от логики, код внутри метода next()
может выглядеть очень сложным и быть подверженным ошибкам. Здесь генераторы предлагают чистое и простое решение.
Подумайте об этом так:
Итератор - это просто причудливо звучащий термин для объекта, у которого есть метод next()
. Таким образом, функция yield-ed будет выглядеть примерно так:
Оригинальная версия:
def some_function():
for i in xrange(4):
yield i
for i in some_function():
print i
По сути, это то, что интерпретатор Python делает с приведенным выше кодом:
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
Чтобы лучше понять, что происходит за кулисами, цикл for
можно переписать следующим образом:
iterator = some_func()
try:
while 1:
print iterator.next()
except StopIteration:
pass
Это имеет больше смысла или только запутало вас еще больше? :)
Должен заметить, что это это чрезмерное упрощение для наглядности :)
На выход
сайта сводится к двум простым фактам:
выход
сайта в любом месте внутри функции, то функция не возвращает через "возврат" заявление. Вместо, его сразу возвращает ленивый глаз "кредитная карта" по объекту называется генераторомсписок
или set
или спектр
или словарь-посмотреть, с встроенный протокол для посещения каждого элемента в определенном порядке.В двух словах: генератор ленивый, пошагово-в ожидании списка, и выход
отчетность позволяют использовать обозначения функции программы список значений генератор должен постепенно выплюнуть.
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]]
Позвольте'ы определить функцию makeRange
что's просто, как Python'с`. Звоню makeRange(Н)
возвращает генератора:
def makeRange(n):
# return 0,1,2,...,n-1
i = 0
while i < n:
yield i
i += 1
>>> makeRange(5)
<generator object makeRange at 0x19e4aa0>
Чтобы заставить генератор немедленно возвратить его до значения, вы можете пройти его в список()` (как ты мог любой итерируемый):
>>> list(makeRange(5))
[0, 1, 2, 3, 4]
Приведенный выше пример можно рассматривать как просто создать список которых вы дописываете и вернуться:
# list-version # # generator-version
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 ## indented
return TO_RETURN #>
>>> makeRange(5)
[0, 1, 2, 3, 4]
Есть правда одно существенное отличие,; см. В последнем разделе.
Повторяемое является последней частью постижения список, и все генераторы Iterable, так что они'Снова часто используется так:
# _ITERABLE_
>>> [x+10 for x in makeRange(5)]
[10, 11, 12, 13, 14]
Чтобы лучше почувствовать генераторы, вы можете поиграть с модулем модуле itertools
(обязательно использовать цепи.from_iterable, а не "цепи" при необходимости). Например, вы могли бы даже использовать генераторы, чтобы бесконечно долго выполнять подобные списки, как модуле itertools.граф()
. Вы могли бы реализовать свой собственный деф перечисление(повторяемое): молния(функция count(), повторяемое)
, или же сделать это с помощью инструкции "yield" в цикл while.
Обратите внимание: генераторы могут быть использованы для многих вещей, таких как реализации сопрограмм или недетерминированное программирование или другие элегантные вещи. Однако, в "ленивые списки" с точки зрения я привожу здесь наиболее часто вы найдете.
Это как в "протокол на Python итерации" не работает. То есть, что происходит, когда вы делаете список(makeRange(5))`. Это то, что я описал ранее как-то "ленивый, добавочные список и".
>>> 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 "<stdin>", line 1, in <module>
StopIteration
Встроенная функция следующий()просто называет объектов.функция следующий ()
, который является частью группы «протокол итерации" и на все итераторы. Вы можете вручную использовать функция Next ()
(и другими частями протокола итерации), чтобы реализовать необычные вещи, как правило, в ущерб читабельности, поэтому постарайтесь, чтобы избежать этого...
Как правило, большинство людей не будет заботиться о следующих различий и, вероятно, хотят прекратить чтение здесь.
В Python-сказать, метод - это любой объект, который "не понимает концепцию из цикла" и как список[1,2,3], и *итераторы* - конкретный экземпляр требуемого для цикла как
[1,2,3].реактор ITER()`. В генератор точно так же, как любой итератор, за исключением того, как он был написан (с синтаксисом функции).
Когда вы запрашиваете итератор из списка, он создает новый итератор. Однако, когда вы запрашиваете из итератора итератор (который вам будет редко), он просто дает вам копию себя.
Таким образом, в маловероятном случае, что вы не в состоянии сделать что-то подобное...
> x = myRange(5)
> list(x)
[0, 1, 2, 3, 4]
> list(x)
[]
... то помните, что генератор итераторы; то есть, это один раз использовать. Если вы хотите использовать его, вы должны позвонить "мой_диапазон" (...) снова. Если вам нужно использовать два раза в результате, конвертировать результат в список и сохранить его в переменной x = список(мой_диапазон(5))
. Те, кто абсолютно не нужно клонировать генератора (например, которые делают ужасающе программистские метапрограммирования), можно использовать модуле itertools .футболка
в случае крайней необходимости, поскольку скопировать итератор на Python ПЭП стандарты предложение было отложено.
что означает
выход
сайта это сделать в Python?Ответ Плана/Резюме
- Функция с [
Выход
][1], при вызове возвращает [генератор][2].- Генераторами являются итераторами, потому что они реализуют [итератор протокол][3], так что вы можете перебирать их.
- Генератор также может быть отправил информацию, что делает его концептуально сопрограмма.
- В Python 3, Вы можете делегат от одного генератора к другому в обоих направлениях с
Выход из
.- (Приложение разборы пару ответов, в том числе верхняя, а также рассматривается использование
возвращение
в генераторе.) Генераторы#:выход
законна только внутри определения функции, и включениевыход
в определении функции делает его вернуть генератор. Идея для генераторов приходит из других языков (см. сноску 1) с различными реализациями. В Python'генераторы с выполнения кода [заморожено][4] в точке выхода. Когда генератор вызывается (методы описаны ниже) возобновляет выполнение, а затем замирает на следующий урожай.выход
обеспечивает легкий способ [осуществления итератор протокол][5], определяется следующими двумя методами:__ИТЭР__
идалее
(питон 2) или__следующей__
(в Python 3). Оба этих методов сделать объект iterator, который можно ввести проверку ситератор
абстрактные базы Класса из модуля "коллекции".
>>> def func():
... yield 'I am'
... yield 'a generator!'
...
>>> type(func) # A function with yield is still a function
<type 'function'>
>>> gen = func()
>>> type(gen) # but it returns a generator
<type '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
И, при необходимости, мы можем проверки типа такой:
>>> isinstance(gen, types.GeneratorType)
True
>>> isinstance(gen, collections.Iterator)
True
Особенность итератор
[что понимают][6], Вы можете'т повторно использовать или сбросить его:
>>> list(gen)
['I am', 'a generator!']
>>> list(gen)
[]
Вы'll должны сделать другой, если вы хотите снова использовать ее функциональные возможности (см. сноску 2):
>>> list(func())
['I am', 'a generator!']
Можно получить данные программно, например:
def func(an_iterable):
for item in an_iterable:
yield item
Выше простой генератор также эквивалентен ниже - как в Python 3.3 (и не доступен в Python 2), Вы можете использовать yield [с
][7]:
def func(an_iterable):
yield from an_iterable
Однако, урожай
позволяет делегацию subgenerators,
которые будут объяснены в следующем разделе О делегировании сотрудничества с Sub-сопрограммы.
выход
формы выражения, что позволяет пересылать данные в генератор (см. сноску 3)
Вот пример, обратите внимание на получил
переменная, которая будет указывать на данные, которые передаются в генератор:
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)
Во-первых, мы должны стоять в очереди генератор с функцией встроенной, [следующий
][8]. Он будет
вызов метода соответствующего далее
или __следующей__
, в зависимости от версии
В Python вы используете:
>>> first_year_interest = next(my_account)
>>> first_year_interest
50.0
И теперь мы можем отправлять данные в генератор. ([Рассылка нет
это
тем же самым как вызовом далее
][9].) :
>>> next_year_interest = my_account.send(first_year_interest + 1000)
>>> next_year_interest
102.5
урожай
Итак, напомним, что выход из
доступен в Python 3. Это позволяет нам делегировать
сопрограммы на subcoroutine:
def money_manager(expected_rate):
under_management = yield # must receive deposited value
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'''
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'''
next(manager) # must queue up manager
manager.send(deposited)
while True:
try:
yield from manager
except GeneratorExit:
return manager.close()
И теперь мы можем делегировать функции суб-генератора и его можно использовать генератор как выше:
>>> my_manager = money_manager(.06)
>>> my_account = investment_account(1000, my_manager)
>>> first_year_return = next(my_account)
>>> first_year_return
60.0
>>> next_year_return = my_account.send(first_year_return + 1000)
>>> next_year_return
123.6
Вы можете прочитать больше о точной семантике урожай
в [Пеп 380.][10]
GeneratorExit "закрыть" метод повышает `в точке функция казнь была заморожена. Это также будет называться
дельтак что вы можете поместить любой код очистки, где вы регулируете
GeneratorExit`:
>>> my_account.close()
Вы также можете бросить исключение, которое можно обработать в генераторе или распространяются на пользователя:
>>> import sys
>>> try:
... raise ValueError
... except:
... my_manager.throw(*sys.exc_info())
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
File "<stdin>", line 2, in <module>
ValueError
Я считаю, что я охватила все аспекты следующий вопрос:
что означает
выход
сайта это сделать в Python? Получается, чтовыход
делает много. Я'м уверен, что я мог бы добавить еще больше тщательный примеры этого. Если вы хотите больше или иметь какую-то конструктивную критику, дайте мне знать в комментариях ниже.Приложение:
Критика сверху/принято отвечать**
- Его путают о том, что делает итерируемый, просто используя список в качестве примера. Смотрите мои ссылки выше, но в резюме: повторяемое имеет методИТЭР
возврата **итераторы**. В **итераторы** обеспечивает
.далее(Python 2 или
.следующий(в Python 3) способ, который неявно вызывается
дляпетли, пока он поднимает
StopIteration`, и как только это произойдет, он будет продолжать это делать.- Затем он использует выражение генератор для описания того, что генератор. Поскольку генератор-это просто удобный способ создать итераторы, это только запутывает дело, и мы еще не дошли до
выход
часть.- В управляя исчерпании генераторов он называет `.следующий метод, когда вместо него следует использовать встроенные функции, "далее". Будет соответствующий уровень абстрагирования, потому что его код не работает в Python 3.
- Модуле itertools? Это не имело отношения к тому, что
выход
делает вообще.- Обсуждение методов, которые
выход
предоставляет вместе с выходом новой функциональностиот
в Python 3. Верхний/принято отвечать-это очень неполный ответ.Критика ответа, предполагая, что
выход
в генераторе выражения или понимания.Грамматика позволяет любое выражение в понимании списка.
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
Поскольку урожайность-это выражение, он был разрекламирован как интересно использовать это в осмысленности или выражение генератор - несмотря цитирования не особенно хорошо использовать-дело. Интернет с CPython основных разработчиков [обсуждаем отвратить его пособие][11]. Здесь'ы соответствующее сообщение из списка рассылки:
на 30 января 2017 года в 19:05, Бретт пушки <Бретт на языке Python.орг> писал:
на Солнце, 29 января 2017 года на 16:39 Крейг Родригес <rodrigc в ОС FreeBSD.орг> писал:
я'м хорошо с любым подходом. Оставив вещи, как они есть в Python 3 не годится, ИМХО.
мой голос это будет синтаксис ошибка, поскольку вы'снова не то, чего вы ожидаете от синтаксис.
Я'д согласитесь, что's не самое удачное место для нас, чтобы в конечном итоге, как и любой код опираясь на текущее поведение действительно слишком умен, чтобы быть ремонтопригодны.
В плане попасть туда, мы'будете, вероятно, хотите:
- SyntaxWarning или DeprecationWarning в 3.7
- предупреждение Py3k в 2.7.х
- синтаксис ошибка в 3.8
спасибо, Ник.
-- Ник Когхлан | ncoghlan в gmail.com | Брисбен, Австралия Кроме того, существует нерешенный вопрос (10544), который, кажется, быть направлена в сторону этого никогда быть хорошей идеей (в PyPy, питон реализацией, написанный на Python, уже поднимает предупреждения синтаксис). Итог, пока разработчики с CPython сказать нам в противном случае: Дон'т положить
выход
в генераторе выражения или понимания.# "Возврат" заявление в генераторе
В Питон 2: В функцию генератора, "возврат" заявление не допускается включать
expression_list
. В этом контексте голой "возврат" означает, что генератор делается и будет, потому что StopIterationдолжен быть поднят. Ань expression_list по сути любое количество выражений, разделенных запятыми, по сути, в Python 2, Вы можете остановить генератор с
возвращение, но вы можете'т вернуть значение. В [Питон 3][14]: В функции генератора "возврат" заявление указывает на то, что генератор делается и будет, потому что StopIteration
должен быть поднят. Возвращенным значением (если любой) используется в качестве аргумента для построенияStopIteration и становится StopIteration.атрибут value
.Сноски
- <суб>в языках Клу, Сатер, и значок были указаны в предложении ввести понятие генераторы в Python. Общая идея что функция может поддерживать внутреннее состояние и выход промежуточного точки данных по запросу пользователя. Этот обещал быть превосходит в производительности другие подходы, в том числе Python многопоточность, что это'т даже на некоторых системах.</суб>
- <суб> это означает, например, что xrange объектов (
спектр
в Python 3) разве'титератора, хотя они и повторяемое, потому что они могут быть повторно использованы. Как и списки, методов их
ИТЭР` возвращает итератор объектов.</суб>- <суб>
выход
был первоначально введен в качестве заявление, это означает, что он могла появиться только в начале строки в блоке кода. Теперьвыход
создает выражения yield. https://docs.python.org/2/reference/simple_stmts.html#grammar-token-yield_stmt Это изменение было предложил, чтобы позволить пользователю отправить данные в генератор как одна может принять его. Для передачи данных, один должен быть в состоянии назначить его к чему-то, и для этого, заявление только что выиграл'т работу.</суб>
yield
подобен return
- он возвращает все, что вы ему скажете (как генератор). Разница в том, что при следующем вызове генератора выполнение начинается с последнего вызова оператора yield
. В отличие от return, кадр стека не очищается, когда происходит выход, однако управление передается обратно вызывающей стороне, поэтому ее состояние возобновится при следующем вызове функции.
В случае с вашим кодом функция get_child_candidates
действует как итератор, так что когда вы расширяете список, она добавляет в новый список по одному элементу за раз.
Функция list.extend
вызывает итератор до тех пор, пока он не исчерпает себя. В случае с примером кода, который вы опубликовали, было бы гораздо понятнее просто вернуть кортеж и добавить его в список.
Там's одна дополнительная вещь, чтобы отметить: функцию, которая дает не'т действительно нужно прекратить. Я'написан такой код ве:
def fib():
last, cur = 0, 1
while True:
yield cur
last, cur = cur, last + cur
Затем я могу использовать его в других такой код:
for f in fib():
if some_condition: break
coolfuncs(f);
Это действительно помогает упростить некоторые проблемы, и делает некоторые вещи проще работать.
Для тех, кто предпочитает минимальный рабочий пример, медитировать на этой интерактивной сессии питона:
>>> 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
МНОГАБУКАФ.
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.
Всякий раз, когда вы окажетесь дома список с нуля, выход
каждый кусок вместо.
Это было моей первой "АХА" в момент выхода.
выход
- это приторный так сказать
построить серию материалов
Же поведение:
>>> for square in square_list(4):
... print(square)
...
0
1
4
9
>>> for square in square_yield(4):
... print(square)
...
0
1
4
9
Различное поведение:
Урожайность однопроходные: вы можете только перебрать один раз. Когда функция имеет выход в нем, мы называем его генератор. И итератор является то, что она возвращает. Эти термины являются показательными. Мы теряем удобство контейнера, но обрести мощь серии, что'ов, вычисленные по мере необходимости, и сколь угодно долго.
Урожайность ленивый, он откладывает вычисления. Функция с доходностью в нем не'т на самом деле исполнить все, когда вы называете его. Он возвращает объект итератора, что помнит, где она была прервана. Каждый раз, когда вы называете `следующий () на итератор (это происходит в цикле for) выполнение дюймов вперед до следующего урожая. "возвращение" поднимает StopIteration и завершает цикл (это естественный конец для петли).
Урожайность универсальный. Данные не'т должны быть сохранены все вместе, она может быть доступна по одному за раз. Он может быть бесконечным.
>>> 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
Если вам нужно многоходовки и серии Это'т слишком долго, просто позвони список () на это:
>>> list(square_yield(4))
[0, 1, 4, 9]
Блестящий выбор дать слово, потому что оба значения применяются:
доходность &ампер;mdash; не производить или предоставлять (как и в сельском хозяйстве)
...представить следующие сведения в серии.
доходность &ампер;mdash; не уступить или уступит (как в политической власти)
...отказаться от исполнения процессора до тех пор, пока итератор авансы.
Урожайность дает генератор.
def get_odd_numbers(i):
return range(1, i, 2)
def yield_odd_numbers(i):
for x in range(1, i, 2):
yield x
foo = get_odd_numbers(10)
bar = yield_odd_numbers(10)
foo
[1, 3, 5, 7, 9]
bar
<generator object yield_odd_numbers at 0x1029c6f50>
bar.next()
1
bar.next()
3
bar.next()
5
Как видите, в первом случае Foo` содержит весь список в памяти сразу. Это'ы не имеет большого значения для списка с 5 элементами, но что, если вы хотите получить список 5 миллионов? Это не только огромный людоед памяти, это также стоит много времени, чтобы построить на момент, когда функция вызывается.
Во втором случае, бар
просто дает Вам генератор. Генератор является итерируемым-что означает, что вы можете использовать его в для
петли и т. д., Но каждое значение только один раз обращались. Все значения также не сохраняются в памяти одновременно; генератор объект "не помнит", где он был в циклов в последний раз ты называл его ... таким образом, если вы're через повторяемое на (допустим) считать до 50 миллиардов, Вы не'т придется считать до 50 миллиардов все сразу и магазин в 50 миллиардов чисел, считать через.
Опять же, это довольно надуманный пример, вы, вероятно, будет использовать модуле itertools, если вы действительно хотели, чтобы сосчитать до 50 миллиардов. :)
Это самый простой вариант использования генераторов. Как вы сказали, он может быть использован для создания эффективных комбинаций, используя доходность толкать вещи через стек вызовов, а не использовать какую-то переменную стека. Генераторы также могут быть использованы для специализированных дерева, и всякие другие вещи.
Он возвращает генератор. Я не очень хорошо знаком с Python, но думаю, что это то же самое, что и блоки итераторов в C##39#, если вы с ними знакомы.
Основная идея заключается в том, что компилятор/интерпретатор/что угодно делает некоторую хитрость, чтобы вызывающий мог продолжать вызывать next(), и она будет продолжать возвращать значения - как если бы метод генератора был приостановлен. Теперь очевидно, что вы не можете "приостановить" метод, поэтому компилятор создает машину состояний, чтобы вы помнили, где вы сейчас находитесь и как выглядят локальные переменные. Это намного проще, чем писать итератор самостоятельно.
Есть один тип ответа, что я Дон'т чувствую, был еще, среди многих ответов, которые описывают, как использовать генераторы. Вот программирования теория язык ответа:
На выход
заявление в Python возвращает генератор. Генератор в Python функцию, которая возвращает <и>продолжения</я и GT; (и в частности, типа сопрограмма, но продолжения представляют собой более общий механизм, чтобы понять, что происходит).
Продолжений в теории языков программирования являются гораздо более фундаментальными рода вычисления, но они не так часто используются, потому что они чрезвычайно трудно рассуждать о и также очень трудно реализовать. Но мысль о том, что продолжение есть, проста: это государство вычислений, которая еще не закончена. В этом состоянии, текущие значения переменных, операций, которые должны быть выполнены, и так далее, сохраняются. Затем в какой-то момент позже в программе продолжение может быть вызван, например, что программа's переменных сбрасываются в это состояние и операции, которые были сохранены осуществляются.
Продолжения, в этой более общей форме, может быть реализован двумя способами. В способ вызова/куб. см
, программа's в стек буквально спас а затем, когда продолжение вызывается, стек восстанавливается.
В продолжение стиль прохождения (КП), продолжения просто нормальные функции (только в языках, где функции являются функциями первого класса), что программист явно управляет и проходит вокруг подпрограмм. В этом стиле, государственной программы представлен замыканиями (и переменные, которые происходят, чтобы быть закодированным в них), а не переменные, которые проживают где-то на стек. Функции управления регулирования потока принимают продолжение в качестве аргументов (в некоторых вариантах ХП, функции могут принимать несколько продолжений) и манипулировать поток управления, вызывая их, просто называя их, а затем вернулись. Очень простой пример продолжение прохождения стиле выглядит следующим образом:
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)
В этом (очень упрощенный) пример, программист экономит время обслуживания на самом деле записи файла в продолжение (которое потенциально может быть очень сложной операции с большим количеством деталей, чтобы выписать), а потом проходит, что продолжение (я.е, как в первом классе закрытия) другому оператору, который делает более некоторой обработки, а затем вызывает его в случае необходимости. (Я использую этот шаблон много в фактическое Программирование GUI, либо потому, что это экономит мне строк кода или, что еще более важно, для управления потока управления после событий GUI триггер.)
В остальной части этот пост, без потери общности, осмыслить продолжения, как Роспотребнадзор, потому что это намного легче понять и прочитать.
<БР>
Теперь давайте'поговорим о генераторы в Python. Генераторы представляют собой специфический подвид продолжение. А продолжения состоянии в целом для сохранения состояния расчет (т. е., программа'ы стек вызовов), генераторы способны только сохранить состояние итерации над итераторы. Хотя, это определение является немного вводит в заблуждение некоторых случаях использование генераторов. Например:
def f():
while True:
yield 4
Это явно разумное повторяемое, чье поведение четко определены-каждый раз, когда генератор перебирает его, он возвращает 4 (и так вечно). Но это вовсе'т, вероятно, прототип типа повторяемое, что приходит на ум, когда думаешь итераторов (т. е. для Х в коллекции: выполнить_действие(х)`). Этот пример иллюстрирует мощность генераторов: если что-то итератор, генератор может сохранять состояние своей итерации.
Повторим еще раз: продолжения может сохранить состояние программы's в стек и генераторы могут сохранять состояние итерации. Это означает, что продолжений больше намного мощнее, чем генераторы, а также генераторы много, много проще. Они проще для дизайнера язык для реализации, и они проще для программиста, чтобы использовать (если у вас есть свободное время, попробуйте прочитать и понять это страница о продолжениях и позвонить/чч).
Но вы можете легко реализовать (и осмыслить) генераторы, как простые, конкретном случае продолжение прохождения стиле:
Всякий раз, когда выход
, он говорит функции, чтобы вернуть продолжение. Когда функция вызывается снова, она начинается с того места, где остановились. Так, в псевдо-псевдокод (т. е. не псевдокод, а не код) генератор'ы "далее" метод в основном следующим образом:
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
где выход
ключевое слово на самом деле синтаксический сахар для функции реального генератора, в основном что-то вроде:
def generatorfun(iterable):
if len(iterable) == 0:
raise StopIteration
else:
return (iterable[0], lambda:generatorfun(iterable[1:]))
Помните, что это просто псевдокод и фактической реализации генераторов в Python является более сложным. Но как упражнение, чтобы понять, что происходит, попробуйте использовать продолжение прохождения стиле для реализации объектов генератор без использования выход
сайта.
Вот пример на простом языке. Я буду обеспечивать соответствие между высоким уровнем человеческих понятий до уровня понятий на языке Python.
Я хочу работать на последовательность чисел, но я не'т хотите, чтобы беспокоить меня с созданием этой последовательности, я только хочу сосредоточиться на работе, чего я хочу. Итак, я делаю следующее:
деф'ining функции генератора, т. е. функции, содержащей
выход`.</б>StopIteration
исключение</б> функция генератора надо повышать не исключение. Это'ы поднимаются автоматически, когда функция заканчивается или возвращение
.Это то, что делает генератор (функция, которая содержит выход
); он начинает выполнение, приостанавливается всякий раз, когда он делает выход
, а когда попросили .следующий()
стоимость это продолжается с места последнего. Она идеально подходит по дизайну с итератор протокол Python, который описывает, как последовательно значений запроса.
Наиболее известным пользователем итератор протокол для команды в Python. Поэтому, когда вы делаете:
for item in sequence:
это не'т имеет значения, если последовательность
является списком, строкой, словарь или генератор object, как описано выше, результат такой же: вы читали элементы от последовательности один за другим.
Обратите внимание, что деф'ining функция, которая содержит выход
сайта-это не только способ создать генератор; его's просто самый простой способ, чтобы создать одну.
Для более точной информации, читать о типы iterator, то выход заявление и генераторы в документации Python.
В то время как многие ответы показывают, почему вы'д использовать выход
, чтобы создать генератор, есть более использует для выхода
. Это'ы довольно легко сделать сопрограмма, которая обеспечивает передачу информации между двумя блоками кода. Я выиграл'т повторить любой из образцов, которые уже были даны через выход
, чтобы создать генератор.
Чтобы помочь понять, что такое выход
в следующем коде, вы можете использовать ваш палец, чтобы проследить цикл через любой код, который имеет выход
. Каждый раз, когда палец попадает в выход
, вы должны ждать далее
или отправить
, чтобы быть введены. Когда "дальше" называется, отследить через код, пока вы не нажмете на выход
... код справа от выход
вычисляется и возвращается инициатору..., то вы будете ждать. Когда "далее" вызывается снова, вы выполните еще одну петлю через код. Тем не менее, вы'МР обратите внимание, что в сопрограмма, выход
может также быть использовано с "отправить" ... который будет отправлять значение от абонента в урожайность функции. Если отправить
, то выход
получает значение, и выплевывает ее левой стороне... потом отследить через код, прогрессирует, пока вы не нажмете на выход
снова (возвращение значение в конце, а если "далее" назывался).
Например:
>>> 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()
Есть еще одна выход
и значение (начиная с версии Python 3.3):
yield from <expr>
С ОПТОСОЗ 380 -- синтаксис для предоставления Subgenerator:
синтаксис предлагается для генератор делегировать часть своих операций на другой генератор. Это позволяет фрагмент кода, содержащий 'урожай' должны быть учтены и помещены в другой генератор. Кроме того, subgenerator разрешается возвращать значение, а значение становится доступным для генератор делегирования.
новый синтаксис также открывает некоторые возможности для оптимизации, когда один генератор вновь дает значения, получаемые при помощи другой.
Кроме того, это введем (начиная с версии Python 3.5):
async def new_coroutine(data):
...
await blocking_action()
чтобы избежать сопрограммы путают с обычным генератором (на сегодняшний день выход
используется в обеих).
Однако все великие ответы, немного сложно для новичков.
Я предполагаю, что вы усвоили инструкция return
.
В качестве аналогии, возвращение
и выход
Близнецы. "возврат" означает, что 'возвращение и остановка' а 'урожайность` означает 'возвращение, но по-прежнему'
- Попробуйте получить num_list с
Возвращение
.
def num_list(n):
for i in range(n):
return i
Запустить его:
In [5]: num_list(3)
Out[5]: 0
Видите, у вас только один номер, а не их список. "возвращение" не позволяет вам счастливо царить, как раз и реализует один раз и бросить курить.
- Наступает
выход
Заменить возвращение
с Выход
:
In [10]: def num_list(n):
...: for i in range(n):
...: yield i
...:
In [11]: num_list(3)
Out[11]: <generator object num_list at 0x10327c990>
In [12]: list(num_list(3))
Out[12]: [0, 1, 2]
Теперь, вы выиграли, чтобы получить все цифры.
По сравнению с "Возвращение", который выполняется один раз и останавливается, выход
работает раз вы задумали.
Вы можете интерпретировать возвращение
, как возвращение одного из них
, и выход
как возвращение всех из них. Это называется
итератор`.
- Еще один шаг, мы можем переписать
выход
заявлениевозвращение
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]
Это'ы базовый о урожай
.
Разница между результатами список возвращение
и доходность объекта `` выход:
Вы всегда получите [0, 1, 2] из списка объектов, но только могли получить их от 'объект выход
выход' раз. Так, она имеет объект новое имя генератор
в из[11]: <объект генератор num_list на 0x10327c990>
.
В заключение, в качестве метафоры, чтобы понять это:
возвращение
и выход
Близнецысписок
и генератор
БлизнецыВот некоторые Python и примеры того, как на деле реализовать генераторы, как если Python не предоставляют синтаксический сахар для них:
В качестве генератора в 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)
С точки зрения программирования, итераторы реализуются как гиперссылки.
Для реализации итераторов, генераторов и пулов потоков для параллельного выполнения и т. д. как преобразователи (также называемые анонимные функции), один использует сообщения, отправленные на объект замком, который имеет диспетчерскую, а диспетчер отвечает, чтобы "сообщения и".
http://en.wikipedia.org/wiki/Message_passing
" вследующем" это сообщение было отправлено закрытия, созданные в "ИТЭР" по вызову.
Есть много способов реализовать это расчет. Я использовал мутации, но это легко сделать это без мутации, возвращая текущее значение и следующий урожай.
Вот демонстрация, которая использует структуру R6RS, но семантика абсолютно идентичны в Python'ов. Это'С одной и той же модели вычислений, и только изменение в синтаксисе необходимо переписать его на Python.
Добро пожаловать ракетки В6.5.0.3.
и GT; -> (определение поколения (лямбда (л) (определить доходность (лямбда () (если (нуль? л) 'Конец (пусть ((в (автомобиль л))) (набор! л (ОДО л)) и V)))) (лямбда(м) (корпус м ('урожайность (урожайность)) ('инициализации (лямбда (сведения) (набор! данные л) 'ОК)))))) и GT; -> (определение потока (быт.'(1 2 3))) и GT; -> (поток 'урожайность) 1 и GT; -> (поток 'урожайность) 2 и GT; -> (поток 'урожайность) 3 и GT; -> (поток 'урожайность) 'Конец и GT; -> ((поток 'инициализации) '(б)) 'ОК и GT; -> (поток 'урожайность) 'а и GT; -> (поток 'урожайность) 'б и GT; -> (поток 'урожайность) 'Конец и GT; -> (поток 'урожайность) 'Конец ->
Я собирался пост "стр. 19 из Бизли'с 'питон: справочно' для краткое описание генераторов", но и многие другие уже выложили хорошие описания.
Также обратите внимание, что выход
может быть использован в сопрограммы как двойной их использования в функции генератора. Хотя это вовсе'т то же использовать как ваш фрагмент кода, (доходность) может быть использован в качестве выражения в функции. Когда абонент отправляет значение метода с помощью метода Send()
метод, тогда сопрограмма будет выполнить до следующего (доходность)
заявления встречаются.
Генераторы и сопрограммы-это хороший способ для настройки потока данных типа приложений. Я думал, что было бы полезно знать о другое использование выход
заявление в функциях.
Вот простой пример:
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
Я не разработчик Python, но он смотрит на меня выход
занимает должность программного потока и следующий цикл начнется от "Выход" в положение. Похоже, что его ждет на этой позиции, и как раз перед тем, возвращая значение за пределами, и в следующий раз продолжает работать.
Вроде бы интересная и хорошая способность :Д