Skip to content

Commit 0c05ad8

Browse files
committed
Added 29-generators.md
1 parent ba10ff4 commit 0c05ad8

1 file changed

Lines changed: 175 additions & 0 deletions

File tree

29-generators.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Python Generators
2+
3+
**Video link:**
4+
5+
In this video, we learned how to create custom iterators in Python using generator functions.
6+
7+
**Programs in the Video**
8+
9+
- [Why Generators?](#why-generators)
10+
- [Python Generators](#python-generators-1)
11+
- [Infinite Stream of Data with Generators](#infinite-stream-of-data-with-generators)
12+
13+
---
14+
15+
## Why Generators?
16+
Before we learn about generators, let's see an example of an iterator implemented in Python.
17+
18+
```python
19+
class Even:
20+
def __init__(self, max):
21+
self.n = 2
22+
self.max = max
23+
24+
def __iter__(self):
25+
return self
26+
27+
def __next__(self):
28+
if self.n <= self.max:
29+
result = self.n
30+
self.n += 2
31+
return result
32+
else:
33+
raise StopIteration
34+
35+
numbers = Even(10)
36+
37+
print(next(numbers))
38+
print(next(numbers))
39+
print(next(numbers))
40+
```
41+
42+
**Output**
43+
```
44+
2
45+
4
46+
6
47+
```
48+
49+
This code generates a sequence of even numbers. For this we have created a custom iterator.
50+
51+
For an object to be iterator it should implement:
52+
- The `__iter__()` method - To return an iterator object
53+
- The `__next__()` method - To return the next element in the stream & possibly raise `StopIteration` exception when
54+
there are no values to be returned.
55+
56+
As you can see, the process of creating iterators is both lengthy and counterintuitive. Generators come to the rescue in such situations.
57+
58+
---
59+
60+
## Python Generators
61+
62+
Now, let's implement the same iterator from previous section using a generator.
63+
A generator is simply a function but with slight modification. In generator function, we use the `yield` keyword to get the next item of the iterator.
64+
65+
```python
66+
def even_generator():
67+
n = 0
68+
69+
n += 2
70+
yield n
71+
72+
n += 2
73+
yield n
74+
75+
n += 2
76+
yield n
77+
78+
numbers = even_generator()
79+
80+
print(next(numbers))
81+
print(next(numbers))
82+
print(next(numbers))
83+
```
84+
85+
**Output**
86+
87+
```
88+
2
89+
4
90+
6
91+
```
92+
93+
First, we have created a generator function that has three yield statements. When we call this generator, it returns an iterator object.
94+
95+
Then, we have called the `__next__()` method to retrieve elements from this iterator. The first `yield` returns the value of `n = 2`.
96+
97+
The difference between `return` and `yield` is that the `return` statement terminates the function completely while the `yield` statement pauses the function saving all its states for next successive calls.
98+
99+
So, when we call `yield` for the second and third time, we get `4` and `6` respectively.
100+
101+
Let's make this generator return even numbers till a certain `max` number:
102+
103+
```python
104+
def even_generator(max):
105+
n = 2
106+
107+
while n <= max:
108+
yield n
109+
n += 2
110+
111+
numbers = even_generator(4)
112+
113+
print(next(numbers))
114+
print(next(numbers))
115+
print(next(numbers))
116+
```
117+
118+
**Output**
119+
120+
```
121+
2
122+
4
123+
Traceback (most recent call last):
124+
File "<string>", line 12, in <module>
125+
StopIteration
126+
```
127+
128+
In this case, our generator could generate even numbers only till `4`. So, using `next()` function for the third time raised a `StopIteration` exception.
129+
130+
Notice how we have never explicitly defined the `__iter__()` method, `__next__()` method, or raised a `StopIteration` exception. They are handled implicitly by generators making our program much simpler and easier to understand.
131+
132+
---
133+
134+
## Infinite Stream of Data with Generators
135+
136+
Iterators and generators are generally used to handle a large stream of data--theoretically even an infinite stream of data. These large streams of data cannot be stored in memory at once. To handle this, we can use generators to handle only one item at a time.
137+
138+
Now, let's build a generator to produce an infinite stream of fibonacci numbers. The fibonacci series is a series where the next element is the sum of the last two elements.
139+
140+
```
141+
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,.....
142+
```
143+
144+
```python
145+
def generate_fibonacci():
146+
n1 = 0
147+
yield n1
148+
149+
n2 = 1
150+
yield n2
151+
152+
while True:
153+
n1, n2 = n2, n1 + n2
154+
yield n2
155+
156+
seq = generate_fibonacci()
157+
158+
print(next(seq))
159+
print(next(seq))
160+
print(next(seq))
161+
print(next(seq))
162+
print(next(seq))
163+
```
164+
165+
**Output**
166+
```
167+
0
168+
1
169+
1
170+
2
171+
3
172+
```
173+
If we had used a for loop and a list to store this infinite series, we would have run out of memory.
174+
175+
However, with generators, we can keep accessing these items for as long as we want. It is because we are just dealing with one item at a time.

0 commit comments

Comments
 (0)