0

Have a look at the code:

def main(): for p in test1(): print(p) def test1(): s = set() s.update(range(5)) for p in s: yield p return s 

Why I got only 0,1,2,3,4? The output should be: 0,1,2,3,4 two times (1 for 'yield' and 1 for 'return')

PS: Python-3.4

5
  • The main part of your question is in the code and not visible as its off to the side. Commented Dec 24, 2014 at 18:53
  • 3
    Do you understand what else on for does? It's explained in the docs. Commented Dec 24, 2014 at 18:54
  • 1
    You also yield the actual set in the first function, after you're done yielding all the elements therein. You need to understand that the return is never executed, and even if it is, is never used Commented Dec 24, 2014 at 18:56
  • Didn't know that it was possible to put an else clause in a for loop. Found a nice blog post about it. Commented Dec 24, 2014 at 19:18
  • @Neftas the other pattern I always used to forget was try/except/else, where the else clause is only executed if there are no exceptions. Commented Dec 24, 2014 at 19:39

2 Answers 2

3

A return statement in a Python 3.3+ generator doesn't do what you think it does. The value is not returned to the caller like in a normal function, but added as an attribute on the StopIteration exception the generator raises to signal that it is done iterating. The behavior you're seeing in your loop is unrelated.

First, lets understand the loop behavior. This comes down to a simple fact: The loop variable (e.g. i) doesn't go out of scope when a for loop ends:

for i in range(5): # this loop will print 0 through 4 print(i) print(i) # this line will print 4 again, since 4 it was the last value assigned to i 

Your code is doing exactly this. The else clause you're using does nothing special, since you never break out of the loop. (Neftas's answer explains what an else attached to a loop is for.)

As for where the return value is going, you can find it if you iterate over your generator manually:

gen = test1() print(next(gen)) # prints 0 print(next(gen)) # prints 1 print(next(gen)) # prints 2 print(next(gen)) # prints 3 print(next(gen)) # prints 4 print(next(gen)) # prints set([0,1,2,3,4]) from the last yield statement try: next(gen) except StopIteration as e: print(e.value) # prints set([0,1,2,3,4]) from the return statement 

This isn't a very common usage. The usual way of getting at the returned value is by using the result of a yield from expression in another generator:

def test3(): print(yield from test1()) 

This is a generator that yields all the same values as test1, but it also prints out the value that test1 returns.

I don't think the return idiom is terribly useful in most situations. yield from can be very useful in recursive or otherwise complex generators, but I've never found a need to return a value from one.

If you want more information about the yield from expression and the return value from generators, read PEP 380, which describes the new features that were added in Python 3.3.

Sign up to request clarification or add additional context in comments.

1 Comment

I just wanted to increase the performance of my app by returning the last part of the 'set' and avoid some mess. Thank you!
0

For a discussion of return in a generator, see Blckknght's answer, this is about the else clause in a for loop.

I read a nice article about else clauses in for loops if you don't like the documentation.

The gist of it is that an else clause in a for loop is all about completion, rather than about conditionals. Compare:

if 1: print "True" else: print "False" 

Here, the else clause is executed when the comparison falls through. But:

for i in xrange(5): if i == 123: print "Found it!" break else: print "Value not in list" # output: "Value not in list" 

Here, the else clause gets executed unless the flow of execution hits the break statement:

for i in xrange(5): if i == 4: print "Found it!" break else: print "Value not in list" # output: "Found it!" 

If your remove the break, both strings will be printed. In your code the flow of execution will always reach the else statement, so the code there is run.

2 Comments

Returning a value from a generator is legal in Python 3.3 and later. It has some (obscure) use when coupled with a yield from expression.
@Blckknght Thanks, I didn't know. I cut that part out, but left the rest in case it may prove useful for somebody.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.