0

Consider the following definition for a Python generator:

def gen(x):

    print("-- BEGIN --")
    y = x + 1
    yield y

    #-- 'tail' of the generator
    z = y + 1
    print("z = %d" % z)
    print("-- END --")

    return z

(I) If one uses this generator as in

g1 = gen(1)
print( next(g1) )

the output will be

-- BEGIN --
2

Another next(g1) will produce

z = 3
-- END --

along with a StopIteration exception.

(II) If one exhausts the g1 generator, e.g., via list

g1 = gen(1)
print("result =", list(g1) )

the output will be

-- BEGIN --
z = 3
-- END --
result = [2]

without any exception.

Questions:

  • What happens to the "tail" of the generator in case (I)? When will it be executed, if ever?
  • Why did the tail did not execute in case (I), but executed in case (II)?
  • Is there a standard way to exhaust the generator in a single step, like what list operation does in list(g1)?
  • Is it possible to recover the final returned value, z, after exhaustion?
vaultah
  • 44,105
  • 12
  • 114
  • 143
AlQuemist
  • 1,110
  • 3
  • 12
  • 22
  • Possible duplicate?: https://stackoverflow.com/questions/16780002/return-in-generator-together-with-yield-in-python-3-3 – juanpa.arrivillaga Feb 12 '18 at 18:21
  • So, what are you actually trying to accomplish here, why are you mixing `yield` and `return`? A generator is simple. It starts executing the first time you call `next`, until it reaches a yield. It keeps doing this until there are no yields, or forever potentially. If the function ends without another yield, it simply raises `StopIteration`, but it will still execute until it is finished. `return` in a generator does something special, see the link I posted above – juanpa.arrivillaga Feb 12 '18 at 18:23
  • "Is there a standard way to exhaust the generator in a single step" iterate over it? What is a "single step"? You could call *list*, or whatever. You could also define your own heleper function: `def consume(g): for x in g: pass` which won't populate a data-structure, if that is what you are trying to avoid... – juanpa.arrivillaga Feb 12 '18 at 18:24
  • https://stackoverflow.com/questions/14183803/what-is-the-difference-between-raise-stopiteration-and-a-return-statement-in-gen/30217723 https://stackoverflow.com/questions/34073370/best-way-to-receive-the-return-value-from-a-python-generator – Josh Lee Feb 12 '18 at 18:27
  • @juanpa.arrivillaga : I am not trying to achieve something particular here; just to understand how generators work. – AlQuemist Feb 12 '18 at 19:00
  • @juanpa.arrivillaga : so the "tail" will be executed when one performs a `next` after the last `yield`, and then `return` raises `StopIteration` exception? – AlQuemist Feb 12 '18 at 19:02
  • @juanpa.arrivillaga : By a "single-step exhaustion", I meant something _built-in_ like `z = exhaust(g1)`, instead of `list(g1)`, which would receive also the final returned value. – AlQuemist Feb 12 '18 at 19:04
  • @AlQuemist essentially, for more details, check out that other question I linked to. There is no `exhaust` built-in, but you could try to `consume` recipe in the [`itertools`](https://docs.python.org/3/library/itertools.html#itertools-recipes) documentation. – juanpa.arrivillaga Feb 12 '18 at 19:15
  • But as for the `return` value, in a generator, `return x` will be equivalent to `raise StopIteration(x)`, so to retrieve the value `catch StopIteration as e` and the `x` will be in `e.value`. I don't think that was a use-case in mind, though. – juanpa.arrivillaga Feb 12 '18 at 19:21

0 Answers0