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,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。

 

迭代器

 

posted on 2018-01-27 10:10  elif_syh  阅读(211)  评论(0)    收藏  举报

导航