Pythonyield usage analysis you may have heard that functions with yield are called generator (generator) in Python. what is generator?
Let's move away from generator to present the concept of yield with a common programming question.
How to generate a Fibonacci data column
The Fibonacci data column is a simple recursive series. except for the first and second numbers, any number can be obtained by adding the first two numbers. Using a computer program to output the first N of the Fibonacci number column is a very simple problem. many beginners can easily write the following functions:
Listing 1. simple output of the first N of the Fibonacci number column
def fab(max): n, a, b = 0, 0, 1 while n < max: print b a, b = b, a + b n = n + 1
Run fab (5) and we can get the following output:
>>> fab(5) 1 1 2 3 5
There is no problem with the results, but experienced developers will point out that printing numbers with print in the fab function will lead to poor reusability of the function, because the fab function returns None, other functions cannot obtain the sequence generated by this function.
To improve the reusability of fab functions, it is best not to print the series directly, but to return a List. The second version after the fab function is rewritten is as follows:
Listing 2. output the second version of the first N of the Fibonacci number column
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
You can print out the List returned by the fab function as follows:
>>> for n in fab(5): ... print n ... 1 1 2 3 5
The rewritten fab function can meet the reusability requirement by returning the List, but more experienced developers will point out that the memory occupied by the function during running will increase with the increase of the max parameter, if you want to control memory usage, it is best not to use List
To save the intermediate results, but to iterate through the iterable object. For example, in Python2.x, the code is as follows:
Listing 3. iteration through the iterable object
for i in range(1000): pass
This will generate a List of 1000 elements, and the code:
for i in xrange(1000): pass
Instead of generating a List of 1000 elements, the next value is returned in each iteration, and the memory space is very small. Xrange does not return List, but returns an iterable object.
With iterable, we can rewrite the fab function into a class that supports iterable. below is the third version of Fab:
Listing 4. third version
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()
The Fab class continuously returns the next number of columns through next (), and the memory usage is always constant:
>>> for n in Fab(5): ... print n ... 1 1 2 3 5
However, the code of this version rewritten by class is far less concise than the fab function of the first version. If we want to keep the simplicity of the first version of the fab function and get the iterable effect, yield will come in handy:
Listing 5. use version 4 of yield
def fab(max): n, a, b = 0, 0, 1 while n < max: yield b # print b a, b = b, a + b n = n + 1 '''
Compared with the first version, fab of the fourth version only changes print B to yield B, which achieves the iterable effect while keeping it simple.
Calling the fourth edition of fab is exactly the same as the second edition of fab:
>>> for n in fab(5): ... print n ... 1 1 2 3 5
In short, yield is used to convert a function into a generator. a function with yield is no longer a common function. The Python interpreter regards it as a generator and calls fab (5) instead of executing the fab function, an iterable object is returned! During for loop execution, code in the fab function is executed in each loop. When yield B is executed, the fab function returns an iteration value. during the next iteration, the code continues to run from the next statement of yield B, and the local variable of the function looks exactly the same as before the last interrupted execution, so the function continues to run until yield is encountered again.
You can also manually call the next () method of fab (5) (because fab (5) is a generator object and this object has the next () method ), in this way, we can see the fab execution process more clearly:
Listing 6. execution process
>>> f = fab(5) >>> f.next() 1 >>> f.next() 1 >>> f.next() 2 >>> f.next() 3 >>> f.next() 5 >>> f.next() Traceback (most recent call last): File "
", line 1, in
StopIteration
When the function execution ends, generator automatically throws a StopIteration exception, indicating that the iteration is complete. In a for loop, the loop ends normally without handling the StopIteration exception.
We can draw the following conclusions:
A function with yield is a generator, which is different from a common function. generating a generator looks like a function call, but does not execute any function code until it calls next () (next () is automatically called in the for loop to start execution. Although the execution process is still executed according to the function process, every execution of a yield statement is interrupted and an iteration value is returned. the next statement of yield continues to be executed during the next execution. It seems that a function is interrupted several times by yield during normal execution, and the current iteration value is returned through yield for each interruption.
The advantage of yield is obvious. by rewriting a function as a generator, the iteration capability is achieved. compared to calculating the next () value by using the instance storage status of the class, the code is not only concise, the execution process is clear.
How can I determine whether a function is a special generator function? You can use isgeneratorfunction to determine:
Listing 7. use isgeneratorfunction to judge
>>> from inspect import isgeneratorfunction >>> isgeneratorfunction(fab) True
Note that fab and fab (5) are distinguished. fab is a generator function, and fab (5) is a generator returned by calling fab, like the difference between class definition and class instance:
Listing 8. class definition and class instance
>>> import types >>> isinstance(fab, types.GeneratorType) False >>> isinstance(fab(5), types.GeneratorType) True
Fab cannot be iterated, while fab (5) can be iterated:
>>> from collections import Iterable >>> isinstance(fab, Iterable) False >>> isinstance(fab(5), Iterable) True
Each time you call the fab function, a new generator instance is generated, and each instance has no influence on each other:
>>> f1 = fab(3) >>> f2 = fab(5) >>> print 'f1:', f1.next() f1: 1 >>> print 'f2:', f2.next() f2: 1 >>> print 'f1:', f1.next() f1: 1 >>> print 'f2:', f2.next() f2: 1 >>> print 'f1:', f1.next() f1: 2 >>> print 'f2:', f2.next() f2: 2 >>> print 'f2:', f2.next() f2: 3 >>> print 'f2:', f2.next() f2: 5
Function of return
In a generator function, if there is no return, it is executed until the function is complete by default. if return is executed, the StopIteration is directly thrown to terminate the iteration.
Another example
Another yield example is from file reading. If you call the read () method directly on a file object, unpredictable memory usage may occur. A good way is to use a fixed-length buffer to constantly read the file content. With yield, you can easily read files without having to write iteration classes for reading files:
Listing 9. Another yield example
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
The above briefly introduces the basic concepts and usage of yield. yield has more powerful usage in Python 3, which will be discussed in subsequent articles.
Note: all the code in this article has been debugged in Python 2.7
The above is a detailed explanation of Python yield usage. For more information, see other related articles in the first PHP community!