★ this article is a part of the manuscript being written. Please have a look at it first. "
9.7 generator
The Generator always exists in Python in an elegant and concise way. From it, we can see that the function is the shadow of the "first kind of object", and we can also feel the programming concept of "great road to simplicity".
The method of defining the generator is very simple, just use the yield keyword. The word yield means "production and production" in Chinese. In Python, as a keyword, it is the symbol of generator.
>>> def gen(): ... yield 0 ... yield 1 ... yield 2 ... >>> g = gen() >>> g <generator object gen at 0x7f86d1936ac0> >>> type(g) <class 'generator'> >>> g.__class__ <class 'generator'> >>> g.__class__.__class__ <class 'type'>
After defining the function gen(), execute g = gen() to get the generator - the function gen() here is obviously different from the function learned in Chapter 7. Moreover, generators G are objects - everything is an object.
>>> hasattr(g, '__iter__') True >>> hasattr(g, '__next__') True
Although it is not explicitly written out in the function gen()__ iter__ () and__ next__ (), only the yield statement is written, but the object g obtained from it has become an iterator -- the first conclusion: a generator must be an iterator (obviously, an iterator is not necessarily a generator).
In this case, of course, the following operations can be carried out:
>>> next(g) 0 >>> next(g) 1 >>> next(g) 2 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
These performances are exactly the same as the iterators learned in section 9.6.
In the function gen(), the keyword yield initiates a statement to return the specified object. From this point of view, it is quite similar to return. Of course, there must be differences between the two. Let's compare them.
>>> def r_return(n): ... print("you took me.") ... while n > 0: ... print("before return.") ... return n ... n -= 1 ... print('after return.') ... >>> rreturn = r_return(3) you took me. before return. >>> rreturn 3
In function R_ In return (), there are other statements before and after the return n statement. From the execution effect, when return is encountered, the value will be returned and the execution of the code block in the function body will be ended. So the two statements after the return statement are not executed at all.
Change return to yield:
>>> def y_yield(n): ... print("you took me.") ... while n > 0: ... print("before yield.") ... yield n ... n -= 1 ... print('after yield.') ... >>> yyield = y_yield(3) # (1) >>> yyield.__next__() # (2) you took me. before yield. 3 # (3)
Comment (1) gets the generator object referenced by the variable yyield, but the function y_ The statement in yield () is not executed, but until annotation (2) executes the generator object__ next__ () method starts executing statements in the function body. It can be seen from the returned result that when the yield statement is executed, the result 3 at this time is returned (as shown in note (3)). At this time, the function body does not jump out, and you can continue to execute the next time__ next__ () method.
>>> yyield.__next__() # (4) after yield. before yield. 2 >>> yyield.__next__() after yield. before yield. 1 >>> yyield.__next__() # (5) after yield. Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
When executing comment (4) (pay attention to the returned result and combine with the code in the function body of gen()), continue to execute downward from the last yield position in the function gen().
This shows that the function of yield is not only to return the specified object, but also to "pause" the function. It can be vividly said to be "suspended", but not to end the function. When "activated" again, the execution continues from the "suspended" position.
Until note (5), when n no longer satisfies the loop condition, it ends and an exception is reported -- this exception is the same as the exception in the iterator.
Let's use yield to write a generator for generating Fibonacci sequence to see what's special about this writing method than that in section 9.6.
Code example:
#coding:utf-8 ''' filename: fibsgenerator.py ''' def fibs(): prev, curr = 0, 1 while True: yield prev prev, curr = curr, prev + curr if __name__ == "__main__": import itertools print(list(itertools.islice(fibs(), 10)))
Execution result:
% python fibsgenerator.py [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
From the number of lines and form of code, it is concise, straightforward, readable and easy to understand!
Look at the function. Fibs () gets a generator that can generate Fibonacci numbers. Note that no parameters are provided here, which means that we get this generator, which contains infinite Fibonacci numbers (the key is the while True infinite loop in fibs() function). itertools.islice() is a function that intercepts a finite sequence from an infinite sequence, and then uses list() to read the resulting finite number of generator elements into memory and convert them into a list.
In Python, in addition to writing a function containing the yield keyword and using it to get the generator, you can also use the "generator resolver" to get the generator object.
>>> [x ** 2 for x in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
This is list parsing (see Chapter 6, section 6.4.1), and the result is a list, not a generator object. With a few changes:
>>> gt = (x ** 2 for x in range(10)) >>> gt <generator object <genexpr> at 0x7f86d1936b30>
Just change [] in the list parsing to () to get the generator object. Note, however, that it is not usually called "tuple resolution", but generator resolution.
>>> gt.__next__() 0 >>> gt.__next__() 1 >>> gt.__next__() 4 >>> list(gt) [9, 16, 25, 36, 49, 64, 81]
The generator object obtained by "generator parsing" is exactly the same as the previous generator object.
The generator is powerful. It allows you to write stream code with fewer intermediate variables and data structures, making memory more efficient.
★ self study suggestions Class is an important content of object-oriented programming. Every developer should master and skillfully apply it. Although Chapters 8 and 9 have introduced a lot of knowledge about classes, this is only the entry level (don't be confused by the title of Chapter 9. The so-called "advanced" is relative to Chapter 8). More knowledge needs to be learned by readers with the help of official documents or other materials in combination with the needs of practice - there is no end to learning. Facing the "boundless sea of learning", only with the ability of self-study can we "ride the wind and waves", which is the original intention of this book to advocate self-study. Readers are also invited to keep their faith at all times. "