Return to generator along with Python 3.3 release

There was an error in Python 2 when the return was along with the exit in the function definition. But for this code in Python 3.3

def f(): return 3 yield 2 x = f() print(x.__next__()) 

there is no error returned in the function with profitability. However, when the __next__ function is __next__ , then a StopIteration exception is thrown. Why does not return only the value 3 ? Is it somehow ignored?

+44
python generator
May 27 '13 at 20:16
source share
3 answers

This is a new feature in Python 3.3 (as the comment notes, it doesn't even work in 3.2). Just as return in the generator has long been equivalent to raise StopIteration() , return <something> in the generator is now equivalent to raise StopIteration(<something>) . For this reason, the exception you see must be printed as StopIteration: 3 , and the value is available through the value attribute of the exception object. If delegated to the generator using the syntax (also new) yield from , this is the result. See PEP 380 for details.

 def f(): return 1 yield 2 def g(): x = yield from f() print(x) # g is still a generator so we need to iterate to run it: for _ in g(): pass 

Prints 1 , but not 2 .

+50
May 27 '13 at 20:25
source share

The return value is not ignored, but the generators give only the values, and return just ends the generator, in this case earlier. In this case, advancing the generator never reaches the yield .

Whenever an iterator reaches the "end" of values ​​to get, a StopIteration must be raised. Generators are no exception. Starting in Python 3.3, any return becomes an exception value:

 >>> def gen(): ... return 3 ... yield 2 ... >>> try: ... next(gen()) ... except StopIteration as ex: ... e = ex ... >>> e StopIteration(3,) >>> e.value 3 

Use the next() function to move iterators instead of directly calling .__next__() :

 print(next(x)) 
+25
May 27 '13 at 20:18
source share

This answer is completely unrelated to the question, but it can be useful to people who got here after a web search.

Here is a small helper function that will turn any possible return value into a received value:

 def generator(): yield 1 yield 2 return 3 def yield_all(gen): while True: try: yield next(gen) except StopIteration as e: yield e.value break print([i for i in yield_all(generator())]) 

[1, 2, 3]




Or as a decorator:

 import functools def yield_all(func): gen = func() @functools.wraps(func) def wrapper(): while True: try: yield next(gen) except StopIteration as e: yield e.value break return wrapper @yield_all def a(): yield 1 yield 2 return 3 print([i for i in a()]) 

[1, 2, 3]

0
Jun 27 '19 at 4:37
source share



All Articles