Python Generators Explained! (Sort of) – Hacker Noon

Learn python generators by example.

GENERATORS

As Iterators

Using generators (generator functions)

>>> def integers():
i = 1
while True:
yield i
i = i + 1
>>> a = integers()
>>> next(a)
1
>>> next(a)
2
>>> next(a)
3
>>> next(a)
4
>>> next(a)
5
>>>

Assigning a generator to a varialble will automatically create .__next__() or next(generator) method

This will allow you to iterate through the generator values

Converting to list

>>> def yrange(n):
i = 0
while i < n:
yield i
i += 1
>>> y = yrange(5)
>>> list(y)
[0, 1, 2, 3, 4]
>>> list(y)
[]

You can convert a generator to a list however on the process it will lose its value and transfer it to the list.

>> y = yrange(5)
>>> ylist = list(y)
>>> ylist
[0, 1, 2, 3, 4]
>>> ylist
[0, 1, 2, 3, 4]
>>> list(y)
[]

You may assign it to a variable to retain the list. Keep in mind it the original generator will be gone.

The power to send to generators

>>> def whizbang():
for i in range(10):
x = yield i
print('i got {}'.format(x))
>>> bangbang = whizbang()
>>> bangbang.__next__()
0
>>> next(bangbang)
i got None
1
>>> bangbang.__next__()
i got None
2
>>> next(bangbang)
i got None
3
>>> bangbang.__next__()
i got None
4

next(generator) is the same as generator.__next__(). Similar to how vars(class) is the same as class.__dict__.

>>> next(bangbang)
i got None
5

Let’s start sending stuff. Generators have generator.send() method.

>>> bangbang.send('yo')
i got yo
6
>>> bangbang.send('yo yo yo')
i got yo yo yo
7

In this case bangbang.send’s parameter is assigned to x. Since x = yield i, means x = yield send value and yield i

>>>  list(bangbang)
i got None
i got None
i got None
[8, 9]

converting to list is not going to work with .send()

>>> bangbang = whizbang()
>>> next(bangbang)
0
>>> list(bangbang.send('test'))
i got test
Exception: TypeError: 'int' object is not iterable
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 list(bangbang.send('test'))
TypeError: 'int' object is not iterable>>>

you will need to call next(generator) at least once before doing a generator.send()

>>> bangbang = whizbang()
>>> bangbang.send('s')
Exception: TypeError: can't send non-None value to a just-started generator
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 bangbang.send('s')
TypeError: can't send non-None value to a just-started generator

Removing values on generator early

Cleaning the values inside a generator is useful. For example if your application run into unknownerror and that left your application on a dirty state instead of risk writing a wrong data you can just close the generator early.

>>> bangbang = whizbang()
>>> bangbang

>>> bangbang.__next__

>>> bangbang.__next__()
0
>>> bangbang.__next__()
i got None
1>>> bangbang.__next__()
i got None
2>>> bangbang.__next__()
i got None
3>>> bangbang.__next__()
i got None
4>>> bangbang.close()
>>> bangbang.__next__()
Exception: StopIteration:
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
in ()
----> 1 bangbang.__next__()
StopIteration:

As you see above bangbang should iterate until 10 however it stopped at 4 since we called bangbang.close()

Further reading

read original article here