Generator
With a list generation ( [x * x for x in range(10)]
for example), we can create a list directly. However, with memory limitations, the list capacity is certainly limited. Also, creating a list of 1 million elements takes up a lot of storage space, and if we just need to access the first few elements, the vast majority of the space behind it is wasted.
So, if the list element can be calculated according to an algorithm, can we continue to calculate the subsequent elements in the process of the loop? This eliminates the need to create a complete list
, saving a lot of space. In Python, this side loop is computed as a mechanism, called the generator: generator
.
The generator can only generate data when it is called to record the current location
There are a number of ways to create one generator
. The first method is simple, as long as a list of the generated formula is []
changed ()
to create a generator
:
>>> L = [x * x for x in range(10)]>>> L[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]>>> g = (x * x for x in range(10))>>> g<generator object <genexpr> at 0x1022ef630>
The L
difference between creating and making is only the outermost and the one, and the g
[]
()
L
list
other g
generator
.
We can print out list
every element directly, but how do we print out generator
every element?
If you want to print out one, you can next()
get generator
the next return value from the function:
>>> next(g)0>>> next(g)1>>> next(g)4>>> next(g)9>>> next(g)16>>> next(g)25>>> next(g)36>>> next(g)49>>> next(g)64>>> next(g)81>>> next(g)Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration
As we've said, the generator
algorithm is saved, each time it is called, the value of the next(g)
next element is computed until the last element is computed, and g
no more elements are thrown when the StopIteration
error occurs.
Of course, this constant invocation is next(g)
really too perverted, the correct way is to use for
loops, because it generator
is also an iterative object:
>>> g = (x * x for x in range(10))>>> for n in g:... print(n)...0149162536496481
So, we create a generator
post that basically never calls next()
, but iterate over it through the for
loop and don't need to be concerned about StopIteration
the error.
generator
Very powerful. If the calculated algorithm is more complex, and the loop with similar list generation for
cannot be implemented, it can also be implemented with functions.
For example, the famous Fibonacci sequence (Fibonacci), except for the first and second numbers, can be summed up by the top two numbers:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
The Fibonacci sequence is not written in a list, but it is easy to print it out with a function:
def fib(max): n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b n = n + 1 return ‘done‘
Note that the assignment statement:
a, b = b, a + b
Equivalent:
t = (b, a + b)# t是一个tuplea = t[0]b = t[1]
However, you do not have to explicitly write out temporary variables t
to assign values.
The above function can output the first number of Fibonacci sequences N
:
>>> fib(10)11235813213455done
Looking closely, it can be seen that the fib
function is actually a calculation rule that defines the Fibonacci sequence, which can be inferred from the first element, and the subsequent arbitrary elements, which are very similar generator
.
That is, the above function and generator
only one step away. To turn the FIB function into a generator, just print(b)
change yield b
it to:
def fib(max): n,a,b = 0,0,1 while n < max: #print(b) yield b a,b = b,a+b n += 1 return ‘done‘
This is generator
another way of defining it. If a function definition contains a yield
keyword, then the function is no longer a normal function, but a generator
:
>>> f = fib(6)>>> f<generator object fib at 0x104feaaa0>
What is most difficult to understand here is that it is generator
not the same as the execution process of a function. The function is executed sequentially, the statement is encountered return
or the last line of the function is returned. The generator
function, which executes at each invocation, next()
encounters a yield
statement return and executes again from the last yield
statement returned.
data = fib(10)print(data)print(data.__next__())print(data.__next__())print("干点别的事")print(data.__next__())print(data.__next__())print(data.__next__())print(data.__next__())print(data.__next__())#输出<generator object fib at 0x101be02b0>11干点别的事235813
In fib
The example above, we continue to call during the loop yield
, and we are constantly interrupted. Of course, you have to set a condition for the loop to exit the loop, or it will produce an infinite sequence.
Similarly, after changing the function generator
, we basically never use the next()
next return value, but instead use the for
loop to iterate:
>>> for n in fib(6):... print(n)...112358
However for
, when called with a loop generator
, the generator
return value of the statement that is not available is found return
. If you want to get the return value, you must catch the Stopiteration error, which is included in the return value StopIteration
value
:
>>> g = fib(6)>>> while True:... try:... x = next(g)... print(‘g:‘, x)... except StopIteration as e:... print(‘Generator return value:‘, e.value)... break...g: 1g: 1g: 2g: 3g: 5g: 8Generator return value: done
The effect of concurrent operations can also be achieved by yield
implementing a single thread
#_*_coding:utf-8_*___author__ = ‘Alex Li‘import timedef consumer(name): print("%s 准备吃包子啦!" %name) while True: baozi = yield print("包子[%s]来了,被[%s]吃了!" %(baozi,name))def producer(name): c = consumer(‘A‘) c2 = consumer(‘B‘) c.__next__() c2.__next__() print("老子开始准备做包子啦!") for i in range(10): time.sleep(1) print("做了2个包子!") c.send(i) c2.send(i)producer("alex")
Implementing concurrent parallel operations with generators
Python Basic knowledge generator