What are Python Generators?

Iterators (which can be traversed like a list or tuple) are created using Python's generator functions, which also produce a traversal object. It is useful to move through each item in the iterator one at a time. Normal functions are specified as generator functions, but the distinction between the two is that in a normal function, the values are returned using the return keyword, whereas in a generator function, the iterator is executed using yield in place of return

Example

def gen_fun():
    yield 40
    yield 50
    yield 60
   
for i in gen_fun():
    print(i)

10
20
30

A generator function is gen _fun() in the example above. This function returns a value whenever it is invoked and does not utilize the return keyword.

Generator function and normal function are different

1) There is just one function in normal functions, however there are one or more yield functions                  in generator functions.

2) The normal function pauses execution when the generator function is called, and the call is then passed to the generator function.

3) Local variables and their current states are retained between calls.

4) On subsequent calls, StopIteration is invoked automatically when the generator function is    ended.

Generators in Python With a Loop

The loop also makes use of the Generator functions.

Example

def seq(x):
    for i in range(x):
        yield i
     
range_ = seq(10)
print(next(range_))
print(next(range_))
print(next(range_))
print(next(range_))
print(next(range_))
print(next(range_))
print(next(range_))
print(next(range_))
print(next(range_))
print(next(range_))
print(next(range_)) 

Output:

0
1
2
3
4
5
6
7
8
9
Traceback (most recent call last):
  File "abc.py", line 17, in <module>
    print(next(range_))
StopIteration

write your code here: Coding Playground

The function seq will run ten times, and on the eleventh call using next, seq will display the StopIteration error.

However, when using a for loop, use next.

for i in seq(5):
    print(i)

Output:

0
1
2
3
4

write your code here: Coding Playground

It won't display any errors. Exception statements are automatically handled by the for loop.

How Do I Make a Python Generator Function?

There are several approaches to build a generator function in Python.

1. Making use of a yield statement

2.Making use of the Python Generator Expression

Yield Statement

Python's yield keyword allows you to exit a function without erasing a local variable's state. We've already spoken about using yield to build generator functions.

Python Generic Expression

A generator function can be expressed informally as a Generator Expression. A Generator Expression can be easily and quickly implemented in Python.

Since both generator functions and lambda functions are anonymous, they fit the definition's description of a lambda function. However, when it comes to implementation, they differ. List comprehension implements both similarly, with the only difference being that round brackets ('()') are used in place of square brackets ('[]').

List comprehension returns a list of elements, whereas generator expression returns an iterable object. This is the primary and most significant distinction between the two.

Example

x = 10
gen = (i for i in range(x) if i % 2 == 0)

list_ = [i for i in range(x) if i % 2 == 0]

print(gen)
print(list_)
for j in gen:
    print(j)

Output:

<generator object <genexpr> at 0x000002471CF1FAC0>
[0, 2, 4, 6, 8]
0
2
4
6
8

write your code here: Coding Playground

(i for i in range(x) if i % 2 == 0) in this generator expression, the first part is the yield part, and next is the for loop, followed by the if statement.

Use of Generators in Python

1. Ease of Implementation: When compared to iterators, generator functions are simpler to implement. Iterators require the implementation of the iter() and __next__() functions in order to function.

2. Memory Efficient: Generator Functions save a lot of memory when employing generators, making them memory efficient. In contrast to the generator function, which produces one output at a time, a normal function returns a sequence of objects after first creating the sequence in memory.

3. Unlimited Sequence: Everyone is aware that there is no memory that can hold an infinite number of sequences. Generators have a role in this situation. Given that generators can only produce one thing at a time, they can only produce one item at a time, so they can present an infinite stream of data/sequence.

def infinite():
    n = 0
    while True:
        yield n
        n += 1
       
for i in infinite():
    if i%4 == 0:
        continue
    elif i == 13:
        break
    else:
        print(i)

Output:

1
2
3
5
6
7
9
10
11

write your code here: Coding Playground

Conclusion

1) Iterators are created using Python's generator functions, which also provide back an iterator object.

2) A yield statement rather than a return statement is used in the generator function.

3) The generator function allows for the usage of numerous yield statements.

4) While using next to iterate over the generator function, the StopIteration error will appear at the conclusion; however, when using the for loop, there is no error displayed.

5)Generic functions called Generator Expressions are nameless. The only difference between them and a list comprehension is that they utilize round parenthesis rather than square ones.