简体   繁体   中英

yield from a generator that has return <value> statement in it

I have a generator with the return value statement in it. If i use next on it I get the Stopiteration: value from it as expected. However when I use yield from the value is lost.

In [1]: def test():
   ...:     return 1
   ...:     yield 2
   ...:

In [2]: t = test()

In [3]: t
Out[3]: <generator object test at 0x000000000468F780>

In [4]: next(t)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-4-9494367a8bed> in <module>()
----> 1 next(t)

StopIteration: 1

In [5]: def new():
   ...:     yield from test()
   ...:

In [6]: n = new()

In [7]: n
Out[7]: <generator object new at 0x00000000050F23B8>

In [8]: next(n)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-8-1c47c7af397e> in <module>()
----> 1 next(n)

StopIteration:

Is there a way to preserve the value when using yield from ? Is this working as intended or maybe it is a bug ?

By receiving the value sent by the sub-generator in the yield from statement.

Taking a quote from PEP 380 -- Syntax for Delegating to a Subgenerator:

The value of the yield from expression is the first argument to the StopIteration exception raised by the iterator when it terminates.

So with a small tweak, res in the new generator will contain the value of StopIteration raised from the test subgenerator:

def new():
   res = yield from test()
   return res

Now when next(n) is executed you'll get the value in the Exception message:

n = new()

next(n)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-39-1c47c7af397e> in <module>()
----> 1 next(n)

StopIteration: 1

Oh, and as an addendum, you can of course get the 'return' value without it being encapsulated in the StopIteration object by using yield again:

def new():
    res = yield from test()
    yield res

Now calling next(new()) will return the value returned from test() :

next(new())
Out[20]: 1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM