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
Post a Comment