What does the Python keyword yield do? for what? Answer: In order to understand what a yield is, you must understand what a generator is. Before understanding generators, let's walk a little closer to iteration.
iterable object
When you build a list, you can read the list item by item, which is called an iterable:
>>> mylist = [1, 2, 3]
>>> for i in mylist :
... print(i)
1
2
3
mylist is an iterable object. When you use a list comprehension to create a list, you create an iterable object:
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist :
... print(i)
0
1
4
All you can use the for .. in .. syntax is called an iterator: list, string, file... You use them a lot because you can read the elements as you want, but you put all the values are stored in memory, which is not what you want if you have a lot of data.
Builder
The generator is iterable, but you can only read it once because it doesn't keep all the values in memory, it generates data in real time:
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator :
... print(i)
0
1
4
Seems to be no different except replacing [] with (). However, you cannot use for i in mygenerator again, because the generator can only be iterated once: 0 is calculated, then 1 is calculated, then 4 is calculated, one after another...
yield keyword
yield is a keyword similar to return, except that the function returns a generator.
>>> def createGenerator() :
... mylist = range(3)
... for i in mylist :
... yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
4
Example: Fibonacci sequence
The Fibonacci sequence is a very simple recursive sequence. Except for the second one and the second one, any number can be obtained by adding the first two numbers.
Example 1: Elementary Writing
There is no problem with the result. Printing numbers directly with print in the fab function will lead to poor reusability of the function, because the fab function returns None, and other functions cannot obtain the sequence generated by the function (without the return function)
def fab(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n = n + 1
>>> fab(5)
1
1
2
3
5
Example 2: Primary writing with return function
This writing method makes up for the drawbacks of Example 1. If you hesitate to return, a list will take up a lot of memory. If you want to control memory usage, it is better not to use list. For example, the difference between range and xrange
def fab(max):
n, a, b = 0, 0, 1
L = []
while n < max:
L.append(b)
a, b = b, a + b
n = n + 1
return L
>>> for n in fab(5):
... print n
...
1
1
2
3
5
Example 3: Class method to write iterator
Change the method to use __iter__ and next to achieve the method of generating the iterator, but the writing method is too complicated
class Fab(object):
def __init__(self, max):
self.max = max
self.n, self.a, self.b = 0, 0, 1
def __iter__(self):
return self
def next(self):
if self.n < self.max:
r = self.b
self.a, self.b = self.b, self.a + self.b
self.n = self.n + 1
return r
raise StopIteration()
>>> for n in Fab(5):
... print n
...
1
1
2
3
5
Example 4: yield version
Compared with the first version, just change the print to yield. Every time it executes to yield, it returns a generator. The next time the loop is repeated, it starts to execute from the back of yield until it returns to yield again.
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
>>> for n in fab(5):
... print n
...
1
1
2
3
5
Another example of yield:
def read_file(fpath):
BLOCK_SIZE = 1024
with open(fpath, 'rb') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return