1.生成器

列表生成式:

1 l = [i*2 for i in range(10)]
2 print(l)

  通过列表生成式,可以直接创建一个列表,但是收到内存限制,列表容量是有限的。如果创建一个包含100w元素的列表,而我们只需要访问前面几个元素,那么就太占用空间了。如果列表元素可以按照某种算法推算出来,我们是否可以在循环过程中不断推算出后续元素呢,这样就不必创建完整的list,从而节省大量的空间,这样一边循环一边计算的机制称为生成器:generator。

1.1 创建生成器

第一种方式:把列表生成式的[]改成()就可以

1 l = [i*2 for i in range(10)]
2 print(l)
3 
4 l = (i*2 for i in range(10))
5 print(l)
6 
7 运行结果:
8 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
9 <generator object <genexpr> at 0x000001BA440C35E8>

 第二种方式:yield方式

  斐波那契数列,除第一个和第二个数外,任意一个数都可由前两个数相加得到:1,1,2,3,5,8,13,21,34...

1 def fib(max):
 2     n,a,b = 0,0,1
 3     while n<max:
 4         print(b)
 5         a,b = b,a+b
 6         n = n+1
 7     return 'done'
 8 
 9 fib(5)
10 
11 运行结果:
12 1
13 1
14 2
15 3
16 5

解析:a,b = b,a+b

相当于如下所示:

1 a,b = 0,1
2 t = (b,a+b)  #t是一个tuple
3 a = t[0]
4 b = t[1]
5 print(a,b)
6 
7 运行结果:
8 1,1

  把上面案例中的print(b)改成yield b就变成了一个生成器。

1 def fib(max):
 2     n,a,b = 0,0,1
 3     while n<max:
 4         yield b
 5         a,b = b,a+b
 6         n = n+1
 7     return 'done'
 8 
 9 fib(5) #此时这里就没有输出内容了,因为已经变成了一个生成器,可以通过__next__()取值
10 print(fib(5))
11 
12 运行结果:
13 <generator object fib at 0x00000257090E35E8>

 取值:

1 def fib(max):
 2     n,a,b = 0,0,1
 3     while n<max:
 4         yield b
 5         a,b = b,a+b
 6         n = n+1
 7     return 'done'  #异常的时候打印的消息
 8 
 9 t=fib(5)
10 print(fib(5))
11 for i in t:  #使用for循环不会报StopIteration异常
12     print(i)

 使用__next__()超过范围会报错StopIteration,如下

1 def fib(max):
 2     n,a,b = 0,0,1
 3     while n<max:
 4         yield b
 5         a,b = b,a+b
 6         n = n+1
 7     return 'done'
 8 
 9 t=fib(5)
10 print(t.__next__())
11 print(t.__next__())
12 print(t.__next__())
13 print(t.__next__())
14 print(t.__next__())
15 print(t.__next__())
16 
17 运行结果:
18 1
19 1
20 2
21 3
22 5
23 Traceback (most recent call last):
24   File "D:/Python/Project/python3.5/test.py", line 15, in <module>
25     print(t.__next__())
26 StopIteration: done

 异常捕获并观察return后面的值:

1 def fib(max):
 2     n,a,b = 0,0,1
 3     while n<max:
 4         yield b
 5         a,b = b,a+b
 6         n = n+1
 7     return 'done'
 8 
 9 t=fib(5)
10 while True:
11     try:
12         x = t.__next__()
13         print(x)
14     except StopIteration as e:
15         print('异常信息:',e.value)
16         break
17 
18 运行结果:
19 1
20 1
21 2
22 3
23 5
24 异常信息: done

 案例:使用yield在单线程的情况下实现并行运算的效果

首先搞清楚__next__()和send()方法的区别:

1 def gen(name):
 2     print("%s在排队!"%name)
 3     while True:
 4         a = yield
 5         print("[%s]来了,[%s]上车了!"%(a,name))
 6 
 7 t = gen("Tom")
 8 t.__next__()
 9 t.__next__()
10 #next运行生成器,并不给yield赋值
11 t.send("公交车")
12 #send运行生成器并给yield赋值
13 
14 运行结果:
15 Tom在排队!
16 [None]来了,[Tom]上车了!
17 [公交车]来了,[Tom]上车了!

 案例开始:

import time

def consumer(name):
    print("%s准备吃包子啦!"%name)
    while True:
        baozi = yield
        print("包子[%s]来了,被[%s]吃了!"%(baozi,name))

def producer(name):
    c = consumer('A')
    c.__next__()
    print("开始做包子啦!")
    for i in range(3):
        time.sleep(1)
        print("%s做了2个包子!"%name)
        c.send(i)

producer('Andy')


'''
运行结果:
A准备吃包子啦!
开始做包子啦!
Andy做了2个包子!
包子[0]来了,被[A]吃了!
Andy做了2个包子!
包子[1]来了,被[A]吃了!
Andy做了2个包子!
包子[2]来了,被[A]吃了!
'''

  生成器总结:

生成器只有在调用时才会生成相应的数据。
生成器只记录当前位置,只能向后取数据(__next__()),不能向前取数据。
创建一个generator后,基本上不会用__next__()调用,而是通过for循环来进行迭代,并且不需要关心StopIteration的错误。

  2.迭代器

  可以直接作用于for循环的数据类型如下:

  • 集合数据类型:list、tuple、dict、set、str等
  • generator:包括生成器和带yield的generator函数

  这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。可以使用isinstance()判断一个对象是否是Iterable对象。

1 from collections import Iterable
2 
3 
4 print(isinstance([],Iterable))
5 print(isinstance((),Iterable))

   生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

  可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

  可以使用isinstance()判断一个对象是否是Iterator对象:

1 from collections import Iterator
 2 
 3 
 4 print(isinstance([],Iterator))
 5 print(isinstance("",Iterator))
 6 print(isinstance((i for i in range(10)),Iterator))
 7 
 8 运行结果:
 9 False
10 False
11 True

   生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

  把list、dict、str等Iterable变成Iterator可以使用iter()函数:

1 from collections import Iterator
 2 
 3 
 4 print(isinstance(iter([]),Iterator))
 5 print(isinstance(iter(""),Iterator))
 6 print(isinstance((i for i in range(10)),Iterator))
 7 
 8 运行结果:
 9 True
10 True
11 True