Iterators and generators 1, list-generated
List Generation List Comprehensions
is a built-in, Python
very simple but powerful build that you can use to create list
.
For example, to generate list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
can be used list(range(1, 11))
:
>>> list(range(1, 11))[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
But what if you want to build [1x1, 2x2, 3x3, ..., 10x10]
? Method One is the loop:
>>> L = []`>>> for x in range(1, 11):... L.append(x * x)...>>> L[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
But the loop is too cumbersome, and the list generation can use a line of statements instead of loops to generate the above list
:
>>> [x * x for x in range(1, 11)][1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
When writing list generation, put the elements to be generated in x * x
front, followed by the for
loop, you can list
create, very useful, write a few more times, you will soon be familiar with this syntax.
for循环
It can be added later if判断
, so we can filter out only the even squares:
>>> [x * x for x in range(1, 11) if x % 2 == 0][4, 16, 36, 64, 100]
小结:
The use of list generation, can be generated quickly list
, can be deduced from one list
another list
, and the code is very concise.
2. Generator
???? With list generation, 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, which saves a lot of space.在Python中,这种一边循环一边计算的机制,称为生成器:generator。
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 g
only the outermost []和()
, the one, and the L
list
other g
generator
.
We can print out list
every element directly, but how do we print out generato
every element of R?
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
From the front we know a concept: generator
save the algorithm, each call next(g)
, calculate g
The value of the next element, until the last element is computed, no more elements when the error is thrown StopIteration
.
Of course, this constant invocation is next(g)
really too perverted, the correct way is to use for循环
, because it is generator
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 for循环
iterate through it 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, in (Fibonacci)
addition to the first and second numbers, can be added by the first two numbers in any number:
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‘注意,赋值语句: a, b = b, a + b相当于: t = (b, a + b) # t是一个tuple a = t[0] b = t[1]但不必显式写出临时变量t就可以赋值。上面的函数可以输出斐波那契数列的前N个数:>>> fib(10)11235813213455done
Careful observation, it can be seen, fib函数
is actually defined by the Fibonacci sequence of the calculation rules, you can start from the first element, the subsequent arbitrary elements, this logic is actually very similar generator
.
That is, the above function and generator
only one step away. To turn fib函数
generator
it into, just need to print(b)
change to yield b
be able to:
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
:
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"print(fib(6))f = fib(6)print(f.__next__())print(f.__next__())print(f.__next__())执行结果:D:\Python18\venv\Scripts\python.exe D:/Python18/day04/斐波那契数列.py<generator object fib at 0x0000000000BCB3B8>112
Breakpoint Analysis:
(1) defines the fib
function with the incoming parameter max
;
(2) Incoming fib (6)
. 6
Generate a generator as a parameter and assign a value to f
, you can view the memory address of the generator through print (FIB (6))
;
(3) print (f.__next__ ()
The first value of the generator is taken out, and the fib
function is called directly;
(4) Initial value n=0, a=0, B=1
, judging n<6
Execute Yield b
saves the interrupt state of the function and returns the value of b
. print (f.__next__ ()
Executes the result 1
;
(5) Executes print (f.__next__ ()
to take the second value, which jumps back yield b
, which returns the state that was saved when the function was interrupted, and then executes A, B = b,a+b
, at which time a=b, or a=1;b=a+b, B=1
.
(6) Execute n + = 1
, n
value plus 1
, then return to while loop judgment;
(7) Judging 1 < 6
, continue yield b
. continues to save the function break state and returns the value of b
. The result of print (f.__next__ ()
is 1
;
(8) The same, then the third print (f.__next__ ()
takes out the third value, The execution result is 2
. Thus the final direct result is printed as:
1<br/>1<br/>2
What is most difficult to understand here is that it is generator
not the same as the execution process of a function. 函数是顺序执行
, return语句
or the last line of the function statement 返回
. Instead, it generator的函数
executes at each invocation, next()
encounters yield语句返回
, and executes again from 上次返回的yield语句处继续执行
.
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 a function generator
, we basically never use it to next()
get the next return value, but instead use it directly for循环
to iterate:
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"for n in fib(6): print(n)执行结果:112358
But for循环
generator
when you call, you find the statement that you can't get generator
return
返回值
. If you want to get the return value, the 必须捕获StopIteration错误
return value is contained in StopIteration
value
:
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"f = fib(6)while True: try: x = next(f) print(x) except StopIteration as e: print(‘Generator return value:‘, e.value) break执行结果:f: 1f: 1f: 2f: 3f: 5f: 8Generator return value: done
You can also achieve yield
the effect of concurrency in a single-threaded scenario: The following execution is equivalent to achieving concurrency in the serial process and also as a co-operation.
#!/usr/bin/python# _*_ coding:utf-8 _*_# Aothr: Kimimport timedef consumer(name): print("%s 准备吃包子啦!" %name) while True: baozi = yield print("包子[%s]来了,被[%s]吃了!" %(baozi,name))def producer(name): c = consumer(‘A‘) #生成一个c的生成器 c2 = consumer(‘B‘) #生成一个c2的生成器 c.__next__() #执行producer()时,会直接调用到consumer,然后打印A 准备吃包子啦!到了yield直接中断 c2.__next__() #同上,打印B 准备吃包子啦! print("老子开始准备做包子啦!") for i in range(3): #循环0~2的序列 time.sleep(1) print("做了2个包子!") c.send(i) #此时使用send将i的值发送给yield,继续执行 print("包子[%s]来了,被[%s]吃了!" %(baozi,name)),执行结果为:包子[0]来了,被[A]吃了!while True死循环又到了yield,中断函数。执行下一步。 c2.send(i) #同上,执行结果为:包子[0]来了,被[B]吃了!至此i=0执行完毕,继续下一个i=1的循环。producer("alex")执行结果:A 准备吃包子啦!B 准备吃包子啦!老子开始准备做包子啦!做了2个包子!包子[0]来了,被[A]吃了!包子[0]来了,被[B]吃了!做了2个包子!包子[1]来了,被[A]吃了!包子[1]来了,被[B]吃了!做了2个包子!包子[2]来了,被[A]吃了!包子[2]来了,被[B]吃了!
3. iterators
A class is a collection of data types, such as list、tuple、dict、set、str
;
One type is generator
, including generators and bands yield
generator function
.
These can be 直接
applied to a system for循环的对象
called an iterative object: Iterable
.
You can use to isinstance()
determine whether an object is an Iterable
object:
>>> from collections import Iterable>>> isinstance([], Iterable)True>>> isinstance({}, Iterable)True>>> isinstance(‘abc‘, Iterable)True>>> isinstance((x for x in range(10)), Iterable)True>>> isinstance(100, Iterable)False
The generator can be used not only for循环
, it can also be next()函数
called continuously and return the next value until the last throw StopIteration
error indicates that the next value cannot continue to be returned.
next()函数
the object that can be called and constantly returns the next value is called 迭代器:Iterator
.
You can use to isinstance()
determine whether an object is an Iterator
object:
>>> from collections import Iterator>>> isinstance((x for x in range(10)), Iterator)True>>> isinstance([], Iterator)False>>> isinstance({}, Iterator)False>>> isinstance(‘abc‘, Iterator)False
Generators are Iterator
objects, but list、dict、str
although Iterable
they are, they are not Iterator
.
Turn the list、dict、str
wait Iterable
into a Iterator
function you can use iter()
:
>>> isinstance(iter([]), Iterator)True>>> isinstance(iter(‘abc‘), Iterator)True
You might ask, why is the data type list, dict, str
, etc. not Iterator
?
This is because the Iterator
object for Python
represents a data flow, and the Iterator
object can be next ()
The function calls and returns the next data continuously until the stopiteration
error is thrown without data. You can think of this data stream as an ordered sequence, but we can't know the length of the sequence in advance, but we will only continue to calculate the next data on demand through the next ()
function, so the calculation of Iterator
is inert, It is calculated only if you need to return the next data.
Iterator
can even represent an infinitely large stream of data, such as the whole natural number. Using list
is never possible to store the entire natural number.
Summary
Any object that can be used for the for Loop
is iterable type
;
Any object that can act on the next () function
is Iterator
types, which represent a sequence of lazy computations,
Collection data types such as list, dict, str
, and so on are iterable
but not Iterator
, but you can get a Iterator
object through the ITER () function
. for Loop
for
Python
is essentially implemented by calling the next () function
, for example:
for x in range(5): print(x)实际上完全等价于:# 首先获得Iterator对象:it = iter([0,1, 2, 3, 4])# 循环:while True: try: # 获得下一个值: x = next(it) except StopIteration: # 遇到StopIteration就退出循环 break
Python starter (eight) iterator and generator