Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between function and generator?

I reviewed the question https://stackoverflow.com/questions/29864146/difference-between-function-and-generator but it is marked as duplicate. So I am posting my version of the answer here.

The original question was:

I am looking for the exact difference between normal functions and generators. I have already googled it. But all the results are quite confusing. I am a beginner, so I am expecting some short and precise example.

I tried this What is the difference between normal function and generator function? but no use.

I have gone through this What does the "yield" keyword do? but it's purely talking about generators, there is nothing about the difference between functions and generators. I need some help?

like image 600
no coder Avatar asked Apr 25 '15 11:04

no coder


1 Answers

In principle generators are memory efficient for its lazy evaluation.

A generator is very similar to a function that returns an array, in that a generator has parameters, can be called, and generates a sequence of values. However, instead of building an array containing all the values and returning them all at once, a generator yields the values one at a time, which requires less memory and allows the caller to get started processing the first few values immediately.

In short, a generator looks like a function but behaves like an iterator.

from itertools import count

itertools provide count to generate infinite stream of integer. You can give start and step to tell starting and stepping value for the generated stream. I am going to use this in the following example.

for i in count(start=0, step=1):    
    print i

A simple example to generate list of even numbers.

Build and return a list:

def find_even_number_function(number_stream):
    even_number = []
    for n in number_stream:
        if n % 2 == 0:
            even_number.append(n)
    return even_number

for i in find_even_number_function(count()):
    print i

The code is quite simple and straightforward, but its builds the full list in memory. This is clearly not acceptable in our case, because we cannot afford to keep all infinite integers in memory. As you can see the function will never stop. In this case we are going to use generator.

A generator that yields items instead of returning a list

def find_even_number_generator(number_stream):
    for n in number_stream:
        if n % 2 == 0:
            yield n

for i in find_even_number_generator(count()):
    print i

Note that the expression of the number generation logic is clear and natural. It is very similar to the implementation that built a list in memory, but has the memory usage characteristic of the iterator implementation.

The performance improvement from the use of generators is the result of the lazy (on demand) generation of values, which translates to lower memory usage. Furthermore, we do not need to wait until all the elements have been generated before we start to use them. This is similar to the benefits provided by iterators, but the generator makes building iterators easy.

So in simple words, if you use a normal function for the above case, you will run out of memory. Alternatively if you use generator function you will run out of time.

like image 156
no coder Avatar answered Nov 11 '22 08:11

no coder