Python学习-生成器和迭代器
生成器
列表生成式
列表生成式是python内置的强大的可以用来创建list的生成式
举个例子:要生成list[0,1,2,3,4,5,6,7,8,9]可以用list(range(0,10)),但要生成[0*0 ,1*1 ,2*2 ,...... 9*9]怎么做呢?
1 l = [] 2 for x in range(0,10): 3 l.append(x * x) 4 print(l)
可以用上面的循环实现。如果我们用列表生成式可以用一行代码实现上面的功能
1 l = [x * x for x in range(0,10)] 2 print(l)
for循环后面还可以加上if判断,下面的代码实现筛选出偶数的需求
1 l = [x for x in range(0,10) if x % 2 == 0] 2 print(l)
for循环还可以进行嵌套
1 l = [x * y for x in range(1,4) for y in range(0,3)] 2 print(l)
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,第一种方法把列表生成式的[ ] 改为( ),就可以创建一个generator
1 l = [x * x for x in range(0,10)] 2 print(l) 3 4 g = (x for x in range(0,10)) 5 print(g) #<generator object <genexpr> at 0x0000000B4E2CCD00>
l 是一个list,而 g 是 generator ,我们可以直接打印出 list 的每个元素,但如何打印generator的每个元素呢?
如果要一个一个打印出来,可以用next() 函数获得generator的下一个返回值
1 print(next(g)) 2 print(next(g)) 3 print(next(g)) 4 print(next(g)) 5 print(next(g)) 6 print(next(g)) 7 print(next(g)) 8 print(next(g)) 9 print(next(g)) 10 print(next(g))
打印出来是:
0
1
2
3
4
5
6
7
8
9
generator保存的是算法,每次调用next(g),就计算出 g 的下一个元素的值,直到计算出最后一个元素,没有更多元素时,抛出StopIteration错误
我们还可以用for 循环,generator也是可迭代对象
1 g = (x for x in range(0,10)) 2 for i in g: 3 print(i) 4 5 0 6 1 7 2 8 3 9 4 10 5 11 6 12 7 13 8 14 9
所以我们在创建一个generator后,基本上是通过for 循环来迭代它,并且不用关心StopIteration的错误。
generator 还可以用函数来实现:
比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:0,1,1,2,3,5,8,13......
1 def fib(max): # 0 1 1 2 3 5 8 13 2 n, before, after = 0, 0, 1 3 while n < max: 4 print(before) 5 before, after = after, after + before 6 n = n + 1 7 fib(8) 8 9 0 10 1 11 1 12 2 13 3 14 5 15 8 16 13
fib 函数实际上是定义了斐波那契数列的推算规则,可以从第一个元素开始,推算出后续的元素,这种逻辑和generator非常类似。
上面的函数要从fib函数变成generator,只需要把 print(before) ,变成yield befor就可以了
1 def fib(max): # 0 1 1 2 3 5 8 13 2 n, before, after = 0, 0, 1 3 while n < max: 4 yield before 5 before, after = after, after + before 6 n = n + 1 7 for i in fib(8): 8 print(i) 9 10 0 11 1 12 1 13 2 14 3 15 5 16 8 17 13
fib函数没有ruturn关键字,函数返回值是一个生成器对象,fib(8)是一个生成器对象,此时函数并不会执行,只有显式或隐式地调用next函数时,才会真正执行里面的函数
yield的作用就是把一个函数变成一个generator,带有yield的函数不再是一个普通函数,Python解释器会将其视为一个generator,在 for 循环执行时,每次循环都会
执行 fab 函数内部的代码,执行到 yield before时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield before 的下一条语句继续执行,而函数的本地变量看起来和
上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当
前的迭代值。
文件读取
1 def read_file(fpath): 2 BLOCK_SIZE = 1024 3 with open(fpath, 'rb') as f: 4 while True: 5 block = f.read(BLOCK_SIZE) 6 if block: 7 yield block 8 else: 9 return
如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。
迭代器
浙公网安备 33010602011771号