From the most common Fibonacci-cut sequence.
The Fibonacci (FIBONACCI) Number column is a very simple recursive sequence, in addition to the first and second numbers, any one of the numbers can be added by the first two numbers. Using a computer program to output the first N number of Fibonacci numbers is a very simple question, and many beginners can easily write the following functions:
Listing 1. The number of first N in a simple output Fibonacci array
def fab(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n = n + 1
Executing fab (5), we can get the following output:
>>> fab(5)
1
1
2
3
5
The result is no problem, but an experienced developer will point out that printing a number directly in the FAB function results in a poor reusability of the function because the FAB function returns None and the other functions cannot get the sequence generated by the function.
To improve the reusability of fab functions, it is best not to print the columns directly, but instead to return a list. The following is the second version after the FAB function is overwritten:
Listing 2. Output Fibonacci Number The first N numbers second edition
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
The List returned by the FAB function can be printed using the following method:
>>> for n in fab(5):
... print n
...
1
1
2
3
5
The rewritten fab function can meet the requirements of reusability by returning the List, but more experienced developers will point out that the memory consumed by this function will increase with the parameter max, and if you want to control the memory footprint, it is best not to use the list
Listing 3. Iterate through Iterable objects
Causes a List of 1000 elements to be generated, and the code:
A List of 1000 elements is not generated, but the next value is returned in each iteration, and the memory footprint is small. Because Xrange does not return a List, it returns a Iterable object.
With iterable we can rewrite the FAB function to a class that supports iterable, and the following is the third version of Fab:
Listing 4. A 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 memory consumption is always constant:
>>> for n in Fab(5):
... print n
...
1
1
2
3
5
However, with this version of the class rewrite, the code is far less concise than the first version of the Fab function. Yield comes in handy if we want to maintain the simplicity of the first version of the Fab function, while also gaining the iterable effect:
Listing 5. Version fourth using 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
‘‘‘
In the fourth version of Fab and the first edition, only print B was changed to yield B, and the iterable effect was achieved while maintaining simplicity.
The Fab and the second version of Fabs that call version fourth are exactly the same:
>>> for n in fab(5):
... print n
...
1
1
2
3
5
Simply put, the function of yield is to turn a function into a generator, the function with yield is no longer a normal function, the Python interpreter treats it as a generator, and the Call to FAB (5) does not execute the FAB function, but instead returns a Iterab Le Object! When the For loop executes, each loop executes the code inside the FAB function, and when it executes to yield B, the FAB function returns an iteration value, and the next iteration, the code proceeds from the next statement of Yield B, and the local variable of the function looks exactly the same as before the last break, so the function Continue execution until yield is encountered again.
You can also call the next () Method of Fab (5) Manually (because Fab (5) is a generator object with the next () method) so that we can see more clearly the execution process of the Fab:
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 "<stdin>", line 1, in <module>
StopIteration
When the function execution finishes, generator automatically throws the Stopiteration exception, indicating that the iteration is complete. In the For loop, there is no need to handle the stopiteration exception and the loop will end normally.
We can draw the following conclusions:
A function with yield is a generator, which, unlike a normal function, generates a generator that looks like a function call, but does not execute any function code until it calls next () (which is automatically called next () in the For loop) to begin execution. Although the execution process still executes according to the process of the function, each execution to a yield statement is interrupted and an iteration value is returned, and the next execution proceeds from the next statement of yield. It looks as if a function was interrupted several times by yield during normal execution, and each break returns the current iteration value through yield.
The benefits of yield are obvious, and the ability to rewrite a function into a generator yields an iterative capability that computes the value of the next next () rather than the instance of the class, not only the code is concise, but the execution process is exceptionally clear.
How can I tell if a function is a special generator function? You can use Isgeneratorfunction to judge:
Listing 7. Use Isgeneratorfunction to judge
>>> from inspect import isgeneratorfunction
>>> isgeneratorfunction(fab)
True
To distinguish between Fab and fab (5), Fab is a generator function, and fab (5) is a generator returned by a call to fab, like the definition of a class and the difference between an instance of a class:
Listing 8. Class definitions and instances of classes
>>> import types
>>> isinstance(fab, types.GeneratorType)
False
>>> isinstance(fab(5), types.GeneratorType)
True
Fab is not iterative, and Fab (5) is iterative:
>>> from collections import Iterable
>>> isinstance(fab, Iterable)
False
>>> isinstance(fab(5), Iterable)
True
Each call to the FAB function generates a new instance of generator, each of which does not affect 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
The function of return
In a generator function, if there is no return, the default execution to the function is complete, if the return in the execution process, then directly throws Stopiteration terminates the iteration.
Another example
Another example of yield is from file reads. Calling the Read () method directly on a file object causes unpredictable memory consumption. A good approach is to use fixed-length buffers to continuously read the contents of the file. With yield, we no longer need to write an iterative class of read files to easily implement file reads:
Listing 9. 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
There is also a more intuitive example that is easier to understand:
Yield is meant to be generated, but in Python it is understood as a generator, and the usefulness of the generator can be iterated, simplifying many of the computational models (and not knowing how to simplify them).
Yield is an expression that has a return value.
When a function contains yield, it is no longer a normal function, but rather a generator. When the function is called, it is not automatically executed, but pauses, see the first example:
Example 1:
>>> def mygenerator ():
... print ‘start ...’
... yield 5
...
>>> mygenerator () // Called here and did not print start ... indicating that the yield function is not running, that is, pause
<generator object mygenerator at 0xb762502c>
>>> mygenerator (). next () // Call next () to let the function run.
start ...
5
>>>
If multiple yields appear in a function then next () will stop at the next yield, see Example 2:
Example 2:
>>> def fun2 ():
... print ‘first’
... yield 5
... print ‘second’
... yield 23
... print ‘end ...’
...
>>> g1 = fun2 ()
>>> g1.next () // Run for the first time, pause at yield 5
first
5
>>> g1.next () // Run for the second time, pause at yield 23
second
twenty three
>>> g1.next () // runs for the third time, since there is no yield after that, next () will throw an error again
end ...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
Why does yield 5 output 5,yield 23 output 23?
We suspect that yield is an expression and there is a return value.
So does this mean that the return value of yield 5 must be 5? In fact, this is not the case, there is a certain relationship with the Send function, this function is essentially similar to next (), the difference is that send is passed the yield expression value in, and next can not pass a specific value, can only pass the none in, so you can think G.next () and G.send (None) are the same. See Example 3:
Example 3:
>>> def fun ():
... print ‘start ...’
... m = yield 5
... print m
... print ‘middle ...’
... d = yield 12
... print d
... print ‘end ...’
...
>>> m = fun () // Create an object
>>> m.next () // causes the function to execute before the next yield
start ...
5
>>> m.send (‘message‘) // Use send () to pass values
message // send () passed in
middle ...
12
>>> m.next ()
None // visible next () return value is empty
end ...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
There is no mention of yield in the usage of this statement "M = Yield 5", which is described in detail below
1. Functions that contain yield
If you see a function that contains yield, which means that the function is already a generator, its execution will be a lot different from other normal functions. For example, the following simple function:
def h():
print ‘To be brave‘
yield 5
h()
As you can see, after calling H (), the print statement is not executed! This is yield, so how do you get the print statement to execute? This is the question to be discussed later, through the discussion and study later, we will understand how yield works.
2. Yield is an expression
Python2.5 Previously, yield was a statement, but now in 2.5, yield is an expression, such as:
m = Yield 5
The return value of the expression (yield 5) is assigned to M, so that M = 5 is wrong. So how do you get the return value (yield 5)? You need to use the Send (msg) method that is described later.
3. See the principle through the next () statement
Now, let's reveal how yield works. We know that the H () above is called and is not executed because it has a yield expression, so we let it execute through the next () statement. The next () Statement resumes generator execution, and until the next yield expression. Like what:
def h():
print ‘Wen Chuan‘
yield 5
print ‘Fighting!‘
c = h()
c.next()
After the C.next () call, H () begins execution until yield 5 is encountered, so the output is:
Wen Chuan
5 # 5 will only appear in the interactive environment, but not run in the file, I have not figured out why, if you know, please give pointers.
When we call C.next () again, we continue execution until we find the next yield expression. Because there is no yield at the back, the exception is thrown (the following results are also run in the interactive environment):
>>> c.next()
Fighting!
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
4. Send (MSG) and Next ()
Once we see how next () is going to make the function that contains yield, let's look at another very important function, send (msg). In fact, next () and send () function in a certain sense is similar, the difference is that send () can pass the value of the yield expression in, and next () cannot pass a specific value, can only pass the none in. So we can be seen as
C.next () and C.send (None) Act the same.
Take a look at this example:
def h ():
print ‘Wen Chuan’,
m = yield 5 # Fighting!
print m
d = yield 12
print ‘We are together!’
c = h ()
c.next () #equivalent to c.send (None)
c.send (‘Fighting!’) # (yield 5) Expression is given ‘Fighting!’
The result of the output is:
Wen Chuan fighting!
Note that the first call, use the next () statement or send (None), you cannot use Send to send a value other than None, otherwise it will be wrong, because there is no yield statement to receive this value.
5. The return value of Send (msg) and Next ()
Send (MSG) and Next () have return values, their return values are special and return the parameters of the next yield expression. Yield 5, for example, returns 5. Do you understand something here? In the first example of this article, traversing Generator with the for I in Alist actually calls alist each time. Next (), and every time alist. The return value of Next () is exactly the yield parameter, which is what we begin to think is being pressed in. Let's continue with the above example:
def h ():
print ‘Wen Chuan’,
m = yield 5 # Fighting!
print m
d = yield 12
print ‘We are together!’
c = h ()
m = c.next () #m gets the parameter value 5 of yield 5
d = c.send (‘Fighting!’) #d gets the parameter value 12 of yield 12
print ‘We will never forget the date’, m, ‘.’, d
Output Result:
Wen Chuan fighting!
We'll never forget the date 5. 12
6. Throw () and close () interrupt Generator
Interrupt generator is a very flexible technique that can be used to terminate generator by throwing a generatorexit exception. The Close () method works the same, but inside it is called throw (generatorexit). We look at:
def close(self):
try:
self.throw(GeneratorExit)
except (GeneratorExit, StopIteration):
pass
else:
raise RuntimeError("generator ignored GeneratorExit")
# Other exceptions are not caught
Therefore, when we call the close () method and then call next () or send (msg), an exception is thrown:
Traceback (most recent call last):
File "/home/evergreen/Codes/yidld.py", line 14, in <module>
d = c.send (‘Fighting!’) #d gets the parameter value 12 of yield 12
StopIteration
Python yield Usage Summary