[issue35756] Using `return value` in a generator function skips the returned value on for-loop iteration

Steven D'Aprano report at bugs.python.org
Sat Jan 19 00:55:38 EST 2019


Steven D'Aprano <steve+python at pearwood.info> added the comment:

On Fri, Jan 18, 2019 at 12:31:51AM +0000, bryan.koch wrote:

> Thank you both for the clarifications.  I agree these's no bug in 
> `yield from` however is there a way to reference the return value when 
> a generator with a return is invoked using `for val in gen` i.e. when 
> the generator is invoked without delegation?

I don't believe so, because the for loop machinery catches and consumes 
the StopIteration.

Any change to that behaviour would be new functionality that would 
probably need to go through Python-Ideas first.

[...]
> Essentially I have code that looks like
> `
> for value in generator:
>   do thing with value 
>   yield value 
> ` 
> where I need to do something before yielding the value.
> It would be awesome if invoking a generator above would throw a 
> SyntaxError iff it contained a return and it wasn't invoked through 
> `yield from`.

How is the interpreter supposed to know? (I assume you mean for the 
SytnaxError to be generated at compile-time.) Without doing a 
whole-program analysis, there is no way for the interpreter to compile a 
generator:

    def gen():
        yield 1
        return 1

and know that no other piece of code in some other module will never 
call it via a for loop.

> The below isn't valid Python and I'm not sure that it should be but 
> it's what I need to do.
> 
> `
> return_value = for value in generator:
>   do thing with value
>   yield value
> 
> if return_value:
>   do something with return_value
> `

Let me be concrete here. You have a generator which produces a sequence 
of values [spam, eggs, cheese, aardvark] and you need to treat the 
final value, aardvark, differently from the rest:

    do thing with spam, eggs, cheese
    do a different thing with aardvark (if aardvark is a True value)

Am I correct so far?

Consequently you writing this as:

def gen():
    yield spam
    yield eggs
    yield cheese
    return aardvark

Correct?

That's an interesting use-case, but I don't think there is any obvious 
way to solve that right now. Starting in Python 3.8, I think you should 
be able to write:

for x in (final := (yield from gen())):
    do something with x  # spam, eggs, cheese
if final:
    do something different with final  # aardvark

----------

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue35756>
_______________________________________


More information about the Python-bugs-list mailing list