Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 275
scdmb Avatar asked May 27 '13 20:05

scdmb


2 Answers

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.

like image 188
Martijn Pieters Avatar answered Sep 24 '22 14:09

Martijn Pieters


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.

like image 45
jamylak Avatar answered Sep 23 '22 14:09

jamylak