Yield in Python Tutorial: Generator & Yield vs Return Example

What is Python yield?

The yield keyword in python works like a return with the only

difference is that instead of returning a value, it gives back a generator object to the caller.

When a function is called and the thread of execution finds a yield keyword in the function, the function execution stops at that line itself and it returns a generator object back to the caller.

Syntax

yield expression

Description

Python yield returns a generator object. Generators are special functions that have to be iterated to get the values.

The yield keyword converts the expression given into a generator function that gives back a generator object. To get the values of the object, it has to be iterated to read the values given to the yield.

Example: Yield Method

Here is a simple example of yield. The function testyield() has a yield keyword with the string “Welcome to Guru99 Python Tutorials“. When the function is called, the output is printed and it gives a generator object instead of the actual value.

def testyield():
  yield "Welcome to Guru99 Python Tutorials"
output = testyield()
print(output)

Output:

<generator object testyield at 0x00000028265EB9A8>

The output given is a generator object, which has the value we have given to yield.

But we are not getting the message we have to given to yield in output!

To print the message given to yield will have to iterate the generator object as shown in the example below:

def testyield():
  yield "Welcome to Guru99 Python Tutorials"

output = testyield()
for i in output:
    print(i)

Output:

Welcome to Guru99 Python Tutorials

What are Generators in Python?

Generators are functions that return an iterable generator object. The values from the generator object are fetched one at a time instead of the full list together and hence to get the actual values you can use a for-loop, using next() or list() method.

Using Generator function

You can create generators using generator function and using generator expression.

A generator function is like a normal function, instead of having a return value it will have a yield keyword.

To create a generator function you will have to add a yield keyword. The following examples shows how to create a generator function.

def generator():
    yield "H"
    yield "E"
    yield "L"
    yield "L"
    yield "O"

test = generator()
for i in test:
    print(i)

Output:

H
E
L
L
O

Difference between Normal function v/s Generator function.

Let us understand how a generator function is different from a normal function.

There are 2 functions normal_test() and generator_test().

Both the functions are suppose to return back the string “Hello World”. The normal_test() is using return and generator_test() is using yield.

# Normal function
def normal_test():
    return "Hello World"
	
#Generator function
def generator_test():
	yield "Hello World"
print(normal_test()) #call to normal function
print(generator_test()) # call to generator function

Output:

Hello World
<generator object generator_test at 0x00000012F2F5BA20>

The output shows that when you call the normal function normal_test() it returns Hello World string. For a generator function with yield keyword it returns <generator object generator_test at 0x00000012F2F5BA20> and not the string.

This is the main difference between a generator function and a normal function. Now to get the value from the generator object we need to either use the object inside for loop or use next() method or make use of list().

print(next(generator_test()))  # will output Hello World

One more difference to add to normal function v/s generator function is that when you call a normal function the execution will start and stop when it gets to return and the value is returned to the caller. So when the execution starts you cannot stop the normal function in between and it will only stop when it comes across return keyword.

But in case of generator function once the execution starts when it gets the first yield it stops the execution and gives back the generator object. You can use the generator object to get the values and also, pause and resume back as per your requirement.

How to read the values from the generator?

You can read the values from a generator object using a list(), for-loop and using next() method.

Using : list()

A list is an iterable object that has its elements inside brackets.Using list() on a generator object will give all the values the generator holds.

def even_numbers(n):
    for x in range(n):
       if (x%2==0): 
           yield x       
num = even_numbers(10)
print(list(num))

Output:

[0, 2, 4, 6, 8]

Using : for-in

In the example, there is a function defined even_numbers() that will give you all even numbers for the n defined. The call to the function even_numbers() will return a generator object, that is used inside for-loop.

Example:

def even_numbers(n):
    for x in range(n):
       if (x%2==0): 
           yield x       
num = even_numbers(10)
for i in num:
    print(i)

Output:

0
2
4
6
8

Using next()

The next() method will give you the next item in the list, array, or object. Once the list is empty, and if next() is called, it will give back an error with stopIteration signal. This error, from next() indicates that there are no more items in the list.

def even_numbers(n):
    for x in range(n):
       if (x%2==0): 
           yield x       
num = even_numbers(10)
print(next(num))
print(next(num))
print(next(num))
print(next(num))
print(next(num))
print(next(num))

Output:

0
2
4
6
8
Traceback (most recent call last):
  File "main.py", line 11, in <module>
    print(next(num))
StopIteration

Generators are one-time Use

Incase of generators they are available for use only once. If you try to use them again, it will be empty.

For example:

def even_numbers(n):
    for x in range(n):
       if (x%2==0): 
           yield x       
num = even_numbers(10)
for i in num:
    print(i)

print("\n")
print("Calling the generator again: ", list(num))

Output:

0
2
4
6
8
Calling the generator again:  []

In case you want the output to be used again, you will have to make the call to function again.

Example: Generators and yield for Fibonacci Series

The following example shows how to use generators and yield in Python. The example will generate the Fibonacci series.

def getFibonnaciSeries(num):
    c1, c2 = 0, 1
    count = 0
    while count < num:
        yield c1
        c3 = c1 + c2
        c1 = c2
        c2 = c3
        count += 1
fin = getFibonnaciSeries(7)
print(fin)
for i in fin:
    print(i)

Output:

<generator object getFibonnaciSeries at 0x0000007F39C8BA20>
0
1
1
2
3
5
8

Example: Calling Function with Yield

In this example will see how to call a function with yield.

The below example has a function called test() that returns the square of the given number. There is another function called getSquare() that uses test() with yield keyword. The output gives the square value for given number range.

def test(n):
    return n*n

def getSquare(n):
    for i in range(n):
        yield test(i)

sq = getSquare(10)
for i in sq:
    print(i)

Output:

0
1
4
9
16
25
36
49
64
81

When to use Yield Instead of Return in Python

Python3 Yield keyword returns a generator to the caller and the execution of the code starts only when the generator is iterated.

A return in a function is the end of the function execution, and a single value is given back to the caller.

Here, is the situation when you should use Yield instead of Return

  • Use yield instead of return when the data size is large
  • Yield is the best choice when you need your execution to be faster on large data sets
  • Use yield when you want to return a big set of values to the calling function
  • Yield is an efficient way of producing data that is big or infinite.

Yield vs. Return

Here, are the differences between Yield and Return

Yield Return
Yield returns a generator object to the caller, and the execution of the code starts only when the generator is iterated. A return in a function is the end of the function execution, and a single value is given back to the caller.
When the function is called and it encounters the yield keyword, the function execution stops. It returns generator object back to the caller. The function execution will start only when the generator object is executed. When the function is called, the execution starts and the value is given back to the caller if there is return keyword. The return inside the function marks the end of the function execution.
yield expression return expression
No memory is used when the yield keyword is used. The memory is allocated for the value returned.
Very useful if you have to deal with huge data size as the memory is not used. Convenient for very small data size.
The performance is better if the yield keyword is used for large data size. A lot of memory is used if the data size is huge that will hamper the performance.
Execution time is faster in case of yield for large data size. The execution time used is more as there is extra processing done in case if your data size is huge, it will work fine for small data size.

Summary

  • The yield keyword in python works like a return with the only difference is that instead of returning a value, it gives back a generator function to the caller.
  • A generator is a special type of iterator that, once used, will not be available again. The values are not stored in memory and are only available when called.
  • The values from the generator can be read using for-in, list() and next() method.
  • The main difference between yield and return is that yield returns back a generator function to the caller and return gives a single value to the caller.
  • Yield does not store any of the values in memory, and the advantage is that it is helpful when the data size is big, as none of the values are stored in memory.
  • The performance is better if the yield keyword is used in comparison to return for large data size.