python - How yield catches StopIteration exception? -


why in example function terminates:

def func(iterable):     while true:         val = next(iterable)         yield val 

but if take off yield statement function raise stopiteration exception?

edit: sorry misleading guys. know generators , how use them. of course when said function terminates didn't mean eager evaluation of function. implied when use function produce generator:

gen = func(iterable) 

in case of func works , returns same generator, in case of func2:

def func2(iterable):     while true:         val = next(iterable) 

it raises stopiteration instead of none return or infinite loop.

let me more specific. there function tee in itertools equivalent to:

def tee(iterable, n=2):     = iter(iterable)     deques = [collections.deque() in range(n)]     def gen(mydeque):         while true:             if not mydeque:             # when local deque empty                 newval = next(it)       # fetch new value ,                 d in deques:        # load deques                     d.append(newval)             yield mydeque.popleft()     return tuple(gen(d) d in deques) 

there is, in fact, magic, because nested function gen has infinite loop without break statements. gen function terminates due stopiteration exception when there no items in it. terminates correctly (without raising exceptions), i.e. stops loop. so question is: stopiteration handled?

to answer question stopiteration gets caught in gen generator created inside of itertools.tee: doesn't. consumer of tee results catch exception iterate.

first off, it's important note generator function (which function yield statement in it, anywhere) fundamentally different normal function. instead of running function's code when called, instead, you'll generator object when call function. when iterate on generator run code.

a generator function never finish iterating without raising stopiteration (unless raises other exception instead). stopiteration signal generator done, , not optional. if reach return statement or end of generator function's code without raising anything, python raise stopiteration you!

this different regular functions, return none if reach end without returning else. ties in different ways generators work, described above.

here's example generator function make easy see how stopiteration gets raised:

def simple_generator():     yield "foo"     yield "bar"     # stopiteration raised here automatically 

here's happens when consume it:

>>> g = simple_generator() >>> next(g) 'foo' >>> next(g) 'bar' >>> next(g) traceback (most recent call last):   file "<pyshell#6>", line 1, in <module>     next(g) stopiteration 

calling simple_generator returns generator object (without running of code in function). each call of next on generator object runs code until next yield statement, , returns yielded value. if there no more get, stopiteration raised.

now, don't see stopiteration exceptions. reason consume generators inside for loops. for statement automatically call next on , on until stopiteration gets raised. catch , suppress stopiteration exception you, don't need mess around try/except blocks deal it.

a for loop for item in iterable: do_suff(item) equivalent while loop (the difference being real for doesn't need temporary variable hold iterator):

iterator = iter(iterable) try:     while true:         item = next(iterator)         do_stuff(item) except stopiteration:     pass finally:     del iterator 

the gen generator function showed @ top 1 exception. uses stopiteration exception produced iterator consuming it's own signal done being iterated on. is, rather catching stopiteration , breaking out of loop, lets exception go uncaught (presumably caught higher level code).

unrelated main question, there 1 other thing want point out. in code, you're calling next on variable called iterable. if take name documentation type of object get, not safe.

next part of iterator protocol, not iterable (or container) protocol. may work kinds of iterables (such files , generators, types own iterators), fail others iterables, such tuples , lists. more correct approach call iter on iterable value, call next on iterator receive. (or use for loops, call both iter , next @ appropriate times!)

edit: found own answer in google search related question, , thought i'd update point out answer above not true in future python versions. pep 479 making error allow stopiteration bubble uncaught generator function. if happens, python turn runtimeerror exception instead.

this means code examples in itertools use stopiteration break out of generator function need modified. you'll need catch exception try/except , return.

because backwards incompatible change, it's being phased in gradually. in python 3.5, code work before default, can new behavior from __future__ import generator_stop. in python 3.6, code still work, give warning. in python 3.7, new behavior apply time.


Comments

Popular posts from this blog

linux - xterm copying to CLIPBOARD using copy-selection causes automatic updating of CLIPBOARD upon mouse selection -

c++ - qgraphicsview horizontal scrolling always has a vertical delta -