0

From PEP342:

Because generator-iterators begin execution at the top of the generator's function body, there is no yield expression to receive a value when the generator has just been created. Therefore, calling send() with a non-None argument is prohibited when the generator iterator has just started, ...

For example,

>>> def a():
...     for i in range(5):
...         print((yield i))
... 
>>> g = a()
>>> g.send("Illegal")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator

Why is this illegal? The way I understood the use of yield here, it pauses execution of the function, and returns to that spot the next time that next() (or send()) is called. But it seems like it should be legal to print the first result of (yield i)?

Asked a different way, in what state is the generator 'g' directly after g = a(). I assumed that it had run a() up until the first yield, and since there was a yield it returned a generator, instead of a standard synchronous object return.

So why exactly is calling send with non-None argument on a new generator illegal?

Note: I've read the answer to this question, but it doesn't really get to the heart of why it's illegal to call send (with non-None) on a new generator.

Community
  • 1
  • 1
morsecoder
  • 1,479
  • 2
  • 15
  • 24

1 Answers1

2

Asked a different way, in what state is the generator 'g' directly after g = a(). I assumed that it had run a() up until the first yield, and since there was a yield it returned a generator, instead of a standard synchronous object return.

No. Right after g = a() it is right at the beginning of the function. It does not run up to the first yield until after you advance the generator once (by calling next(g)).

This is what it says in the quote you included in your question: "Because generator-iterators begin execution at the top of the generator's function body..." It also says it in PEP 255, which introduced generators:

When a generator function is called, the actual arguments are bound to function-local formal argument names in the usual way, but no code in the body of the function is executed.

Note that it does not matter whether the yield statement is actually executed. The mere occurrence of yield inside the function body makes the function a generator, as documented:

Using a yield expression in a function definition is sufficient to cause that definition to create a generator function instead of a normal function.

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • How can a generator be returned if you haven't executed up until the yield statement? What if you have a yield within an if statement, and the the else doesn't have a yield? – morsecoder Jul 17 '15 at 21:31
  • "How can a generator be returned if you haven't executed up until the yield statement?" Why would that be a problem? – kindall Jul 17 '15 at 21:36
  • @StephenM347: Did you read the PEP? The mere *presence* of a yield *anywhere* inside the function makes the function a generator function. It doesn't matter if execution never reaches the yield. The generator-ness of the function is determined at function definition time, before any of its code is actually executed. – BrenBarn Jul 17 '15 at 21:40
  • @BrenBarn, your last comment there really got to the heart of my misunderstanding! I did read a lot of the PEP, but I didn't find any part that describes it like that. If you put that last comment in the answer, I'll mark it as correct. Also, I gave [this](http://pastebin.com/FvVV7Q8y) a try, is there any way to get value 3 from the initially returned generator? – morsecoder Jul 18 '15 at 01:07
  • @StephenM347: I edited my answer. As for your followup, what do you mean "get value 3 from the initially returned generator"? You do get 3 from the initially returned generator. . . when you advance it. If you're asking whether there's a way to make `a()` return 3 directly, the answer is no, because it returns the generator object instead. If it returned 3, how would you get the generator object to make use of it for more iteration? – BrenBarn Jul 18 '15 at 01:34
  • 1
    @StephenM347: Looking at your example, I see maybe you meant can you get the 3 from `return 3` inside the generator to be yielded instead of raise StopIteration. The answer is again no. In a generator, `yield` is the way to yield values. If you return a value, that value becomes an argument to StopIteration (which is why you get `StopIteration: 3` on your first attempt). You can't have a function that is sometimes a generator and sometimes not; either it is or it isn't, and that's determined when it's defined. – BrenBarn Jul 18 '15 at 01:37