Builder
1. Why do we need a generator
Through the above learning, we can know the list generation formula, and we can directly create a list. However, due to memory constraints, the list capacity is definitely limited. Moreover, creating a list containing 10 million elements not only takes up a lot of storage space, but if we only need to access the first few elements, the space occupied by most of the subsequent elements is wasted.
So, if the list elements can be calculated according to a certain algorithm, can we continuously calculate subsequent elements during the loop? This saves a lot of space by not having to create a complete list. In Python, this mechanism of looping and calculating at the same time is called a generator: generator.
In Python, functions that use yield are called generators.
Different from ordinary functions, a generator is a function that returns an iterator and can only be used for iterative operations. It is easier to understand that a generator is an iterator.
In the process of calling the generator to run, each time it encounters yield, the function will pause and save all current running information, and return the value of yield. And continue running from the current position the next time the next() method is executed.
So how to create a generator?
2. Creating a generator
The simplest and easiest way is to change [] of a list generation to ()
# -*- coding: UTF-8 -*- gen= (x * x for x in range(10)) print(gen)
Output result:
<generator object <genexpr> at 0x0000000002734A40>
The difference between creating a List and a generator is only the outermost [] and (). But the generator does not actually create a list of numbers, but instead returns a generator that "yields" (yields) an item each time it is calculated. The generator expression uses "lazy evaluation" (also translated as "lazy evaluation", I think this method of calling by need is better translated as lazy), and is only assigned when retrieving (evaluated), so it is more memory efficient when the list is long.
So I know how to create a generator, but how do I view the elements inside?
3. Traverse the elements of the generator
According to our thinking, the for loop is used for traversal. By the way, we can try:
# -*- coding: UTF-8 -*- gen= (x * x for x in range(10)) for num in gen : print(num)
That's right, you can traverse it directly like this. Of course, iterators were also mentioned above, so can next() be used to traverse? Of course it's possible.
4. Implement the generator in the form of a function
As mentioned above, the simplest and simplest way to create a generator is to generate a list of [] Change to (). Why is it suddenly created in the form of a function?
In fact, a generator is also an iterator, but you can only iterate it once. This is because they don't store all the values in memory, but generate the values at runtime. You use them by iterating over them, either using a "for" loop, or passing them to any functions and structures that can be iterated over. And in actual application, most generators are implemented through functions. So how do we create it through functions?
Don’t worry, let’s take a look at this example:
# -*- coding: UTF-8 -*- def my_function(): for i in range(10): print ( i ) my_function()
The output result:
0 1 2 3 4 5 6 7 8 9
If we need to turn it into a generator, we only need to change print (i) It is enough to yield i. Let’s take a look at the modified example:
# -*- coding: UTF-8 -*- def my_function(): for i in range(10): yield i print(my_function())
Output result:
<generator object my_function at 0x0000000002534A40>
However, this example is very unsuitable for using a generator and cannot take advantage of the generator. Characteristics, the best application of generators should be: you don't want to allocate a large number of calculated result sets to memory at the same time, especially if the result set also contains loops. Because this will consume a lot of resources.
For example, the following is a generator that calculates the Fibonacci sequence:
# -*- coding: UTF-8 -*- def fibon(n): a = b = 1 for i in range(n): yield a a, b = b, a + b # 引用函数 for x in fibon(1000000): print(x , end = ' ')
The effect of running:
You see, running a parameter like this, It won't be said that there is a stuck state, because this method does not use too many resources. Here, the most difficult thing to understand is that the execution flow of generators and functions is different. Functions are executed sequentially and return when encountering a return statement or the last line of function statements. The function that becomes a generator is executed every time next() is called, returns when encountering a yield statement, and continues execution from the last returned yield statement when executed again.
For example, this example:
# -*- coding: UTF-8 -*- def odd(): print ( 'step 1' ) yield ( 1 ) print ( 'step 2' ) yield ( 3 ) print ( 'step 3' ) yield ( 5 ) o = odd() print( next( o ) ) print( next( o ) ) print( next( o ) ) 输出的结果: step 1 1 step 2 3 step 3 5
As you can see, odd is not an ordinary function, but a generator. During execution, it will be interrupted when it encounters yield, and execution will continue next time. After executing yield three times, there is no more yield to execute. If you continue to print print(next(o)), an error will be reported. So errors are usually captured in the generator function.
5. Print Yang Hui Triangle
After learning the generator, we can directly use the knowledge points of the generator to print Yang Hui Triangle:
# -*- coding: UTF-8 -*- def triangles( n ): # 杨辉三角形 L = [1] while True: yield L L.append(0) L = [ L [ i -1 ] + L [ i ] for i in range (len(L))] n= 0 for t in triangles( 10 ): # 直接修改函数名即可运行 print(t) n = n + 1 if n == 10: break
The output result is:
[1] [1, 1] [1, 2, 1] [1, 3, 3, 1] [1, 4, 6, 4, 1] [1, 5, 10, 10, 5, 1] [1, 6, 15, 20, 15, 6, 1] [1, 7, 21, 35, 35, 21, 7, 1] [1, 8, 28, 56, 70, 56, 28, 8, 1] [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]