coroutine - python yield and return in same function - What does the “yield” keyword do?
python yield expression / python / iterator / generator / yield
What is the use of the
yield keyword in Python, and what does it do?
For example, I'm trying to understand this code1:
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
And this is the caller:
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
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.
The generator type is a sub-type of iterator:
import collections, types issubclass(types.GeneratorType, collections.Iterator) True
And if necessary, we can type-check like this:
isinstance(gen, types.GeneratorType) True isinstance(gen, collections.Iterator) True
>>> list(gen) ['I am', 'a generator!'] >>> list(gen) 
You'll have to make another if you want to use its functionality again (see footnote 2):
>>> list(func()) ['I am', 'a generator!']
One can yield data programmatically, for example:
def func(an_iterable): for item in an_iterable: yield item
def func(an_iterable): yield from an_iterable
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)
first_year_interest = next(my_account) first_year_interest 50.0
next_year_interest = my_account.send(first_year_interest + 1000) next_year_interest 102.5
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?
And now we can delegate functionality to a sub-generator and it can be used by a generator just as above:
my_manager = money_manager(.06) my_account = investment_account(1000, my_manager) first_year_return = next(my_account) # -> 60.0
Now simulate adding another 1,000 to the account plus the return on the account (60.0):
next_year_return = my_account.send(first_year_return + 1000) next_year_return # 123.6
You can also throw an exception which can be handled in the generator or propagated back to the user:
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 6, in money_manager File "<stdin>", line 2, in <module> ValueError
The grammar currently allows any expression in a list comprehension.
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
This means, for example, that
range objects aren't
Iterators, even though they are iterable, because they can be reused. Like lists, their
__iter__ methods return iterator objects.