scdmb
scdmb

Reputation: 15621

Yield vs generator expression - different type returned

There is this code:

def f():
  return 3
  return (i for i in range(10))

x = f()
print(type(x)) # int

def g():
  return 3
  for i in range(10):
    yield i

y = g()
print(type(y)) # generator

Why f returns int when there is return generator statement? I guess that yield and generator expression both returns generators (at least when the statement return 3 is removed) but are there some other rules of function compilation when there is once generator expression returned and second time when there is yield keyword inside?

This was tested in Python 3.3

Upvotes: 5

Views: 511

Answers (4)

Elazar
Elazar

Reputation: 21595

def f():
  return 3
  #unreachable code below
  return (i for i in range(10))

I believe what you meant is:

def f():
  yield 3
  yield from (i for i in range(10))

Upvotes: 2

Martijn Pieters
Martijn Pieters

Reputation: 1121654

As soon as you use a yield statement in a function body, it becomes a generator. Calling a generator function just returns that generator object. It is no longer a normal function; the generator object has taken over control instead.

From the yield expression documentation:

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

When a generator function is called, it returns an iterator known as a generator. That generator then controls the execution of a generator function. The execution starts when one of the generator’s methods is called.

In a regular function, calling that function immediately switches control to that function body, and you are simply testing the result of the function, set by it's return statement. In a generator function, return still signals the end of the generator function, but that results in a StopIteration exception being raised instead. But until you call one of the 4 generator methods (.__next__(), .send(), .throw() or .close()), the generator function body is not executed at all.

For your specific function f(), you have a regular function, that contains a generator. The function itself is nothing special, other that that it exits early when return 3 is executed. The generator expression on the next line stands on its own, it does not influence the function in which it is defined. You can define it without the function:

>>> (i for i in range(10))
<generator object <genexpr> at 0x101472730>

Using a generator expression produces a generator object, just like using yield in a function, then calling that function produces a generator object. So you could have called g() in f() with the same result as using the generator expression:

def f():
    return 3
    return g()

g() is still a generator function, but using it in f() does not make f() a generator function too. Only yield can do that.

Upvotes: 10

BrenBarn
BrenBarn

Reputation: 251373

Returning a generator doesn't make f a generator function. A generator is just an object, and generator objects can be returned by any function. If you want f to be a generator function, you have to use yield inside the function, as you did with g.

Upvotes: 0

jamylak
jamylak

Reputation: 133544

def f():
  return 3
  return (i for i in range(10))

is the same as

def f():
  return 3

The second return statement never gets executed, just by having a generator expression within f does not make it a generator.

Upvotes: 3

Related Questions