生成器
生成器不会把结果保存在一个列表中,而是保存为生成器状态,而后在每次迭代的时候返回一个值,直到迭代完返回StopIteration异常结束。生成器实现可以很好的解决,当列表过长耗费内存等情况。
生成器表达式
生成器表达式与列表解析式相同,只不过是将[]换成了()。
1 | # 列表解析式 |
2 | 2 for i in range(100)] [i** |
3 | [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, 2304, 2401, 2500, 2601, 2704, 2809, 2916, 3025, 3136, 3249, 3364, 3481, 3600, 3721, 3844, 3969, 4096, 4225, 4356, 4489, 4624, 4761, 4900, 5041, 5184, 5329, 5476, 5625, 5776, 5929, 6084, 6241, 6400, 6561, 6724, 6889, 7056, 7225, 7396, 7569, 7744, 7921, 8100, 8281, 8464, 8649, 8836, 9025, 9216, 9409, 9604, 9801] |
4 | |
5 | # 生成器解析式 |
6 | 2 for i in range(100)) (i** |
7 | <generator object <genexpr> at 0x107b0bf68> #返回迭代器 |
8 | 2 for i in range(100)) gen=(i** |
9 | next(gen) |
10 | 0 |
11 | next(gen) |
12 | 1 |
13 | for i in gen: |
14 | '\t') print(i,end= |
15 |
|
16 | 4 9 16 25 36 49 64 81 100 121 144 169 196225 256 289 324 361 400 441 484 529 576 625 676 729784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521 1601681 1764 1849 1936 2025 2116 2209 2304 2401 2500 2601 2704 2802916 3025 3136 3249 3364 3481 3600 3721 3844 3969 4096 4225 4354489 4624 4761 4900 5041 5184 5329 5476 5625 5776 5929 6084 6246400 6561 6724 6889 7056 7225 7396 7569 7744 7921 8100 8281 8468649 8836 9025 9216 9409 9604 9801 |
生成器函数
生成器函数,在函数定义是通过yield来实现。
1 | #实现一个阶乘的生成器 |
2 | def Factorial(): |
3 | 1 ret= |
4 | 1 incr= |
5 | while True: |
6 | yield ret |
7 | 1 incr += |
8 | ret *= incr |
9 | gen=Factorial() |
10 | gen |
11 | <generator object Factorial at 0x107b0bf68> |
12 | type(gen) |
13 | <class 'generator'> |
14 | >>> next(gen) => 1的阶乘 |
15 | 1 |
16 | >>> next(gen) => 2的阶乘 |
17 | 2 |
18 | >>> next(gen) => 3的阶乘 |
19 | 6 |
20 | >>> next(gen) => 4的阶乘 |
21 | 24 |
22 | >>> next(gen) => 5的阶乘 |
23 | 120 |
24 | |
25 | #从列表中读取生成器数据 |
26 | 1,2,3,4,5] lst=[ |
27 | def gen(): |
28 | yield from lst |
29 |
|
30 | gen=gen() |
31 | gen |
32 | <generator object gen at 0x107b46360> |
33 | for i in gen: |
34 | print(i) |
35 |
|
36 | 1 |
37 | 2 |
38 | 3 |
39 | 4 |
40 | 5 |
yield 与 return
yield可以理解为不停的暂停函数的执行,而return则是直接退出函数的执行,我们可以看下yield和return的效果。
1 | def Incr(): |
2 | for i in range(100): |
3 | yield i |
4 | if i >= 10: |
5 | return 'more than 10' |
6 |
|
7 | gen=Incr() |
8 | gen |
9 | <generator object Incr at 0x107b46048> |
10 | for i in gen: |
11 | print(i) |
12 |
|
13 | 0 |
14 | 1 |
15 | 2 |
16 | 3 |
17 | 4 |
18 | 5 |
19 | 6 |
20 | 7 |
21 | 8 |
22 | 9 |
23 | 10 |
24 | |
25 | # 这里被函数中的return退出了生成器函数,但是我们并没有看见return回的字符,而且这里我们也可以得到结论,只要有yield,整个函数就是生成器函数,返回的就是迭代器。 |
26 | # 我们手动来查找下return回的字符在哪 |
27 | |
28 | gen=Incr() |
29 | next(gen) |
30 | 0 |
31 | next(gen) |
32 | 1 |
33 | next(gen) |
34 | 2 |
35 | next(gen) |
36 | 3 |
37 | next(gen) |
38 | 4 |
39 | next(gen) |
40 | 5 |
41 | next(gen) |
42 | 6 |
43 | next(gen) |
44 | 7 |
45 | next(gen) |
46 | 8 |
47 | next(gen) |
48 | 9 |
49 | next(gen) |
50 | 10 |
51 | next(gen) |
52 | Traceback (most recent call last): |
53 | File "<stdin>", line 1, in <module> |
54 | StopIteration: more than 10 #这里return回的字符被作为了异常的一部分 |
生成器支持的方法
1 | help(gen) |
2 | Help on generator object: |
3 | |
4 | Incr = class generator(object) |
5 | | Methods defined here: |
6 | ... |
7 | | close(...) |
8 | | close() -> raise GeneratorExit inside generator. |
9 | | |
10 | | send(...) |
11 | | send(arg) -> send 'arg' into generator, |
12 | | return next yielded value or raise StopIteration. |
13 | | |
14 | | throw(...) |
15 | | throw(typ[,val[,tb]]) -> raise exception in generator, |
16 | | return next yielded value or raise StopIteration. |
17 | ... |
close()
手动关闭生成器函数。
1 | gen=Incr() |
2 | next(gen) |
3 | 0 |
4 | next(gen) |
5 | 1 |
6 | gen.close() |
7 | next(gen) |
8 | Traceback (most recent call last): |
9 | File "<stdin>", line 1, in <module> |
10 | StopIteration |
send()
生成器函数还可以接受外部传入参数,并根据参数来进行结果返回,并且这里也是可以实现最简单的协程(协程可以理解为用户空间控制的程序调度),下面是例子:
1 | def gen1(): |
2 | 'gen1 is running...') print( |
3 |
|
4 | def gen2(): |
5 | 'starting...' val= |
6 | while True: |
7 | yield val recv= |
8 | if recv == 'gen1': |
9 | gen1() |
10 | 'get value {}'.format(recv) val= |
11 |
|
12 | gen=gen2() |
13 | gen |
14 | <generator object gen2 at 0x107b460a0> |
15 | None)) print(gen.send( |
16 | starting... |
17 | 'test')) print(gen.send( |
18 | get value test |
19 | 'gen1')) print(gen.send( |
20 | gen1 is running... |
21 | get value gen1 |
生成器函数可以通过gen.send(None)或者next(gen)来启动生成器函数,执行到第一次yield暂停,返回初始值,接下来的参数可以通过send发送参数并赋值给recv,并通过下面的表达式改变val的赋值。
throw()
向生成器函数送入一个异常,可以自定义一些异常处理。
1 | def thro(): |
2 | while True: |
3 | try: |
4 | yield "test value 1" |
5 | yield "test value 2" |
6 | "it is ok") print( |
7 | except ValueError: |
8 | "we get a ValueError") print( |
9 | except IOError: |
10 | break |
11 | gen=thro() |
12 | gen |
13 | <generator object thro at 0x107b46360> |
14 | next(gen) |
15 | 'test value 1' |
16 | next(gen) |
17 | 'test value 2' |
18 | next(gen) |
19 | it is ok |
20 | 'test value 1' |
21 | gen.throw(ValueError) |
22 | we get a ValueError |
23 | 'test value 1' |
24 | gen.throw(ValueError) |
25 | we get a ValueError |
26 | 'test value 1' |
27 | gen.throw(IOError) |
28 | Traceback (most recent call last): |
29 | File "<stdin>", line 1, in <module> |
30 | StopIteration |
31 | next(gen) |
32 | Traceback (most recent call last): |
33 | File "<stdin>", line 1, in <module> |
34 | StopIteration |
当throw传入异常的时候,触发了except代码块,而后重新返回try执行第一个yield,所以会出现我们传入异常的时候始终出现的都是相同的结果的原因。
总结
1.生成器就是一种迭代器
2.第一次执行生成器,执行完yield,函数会被挂起,所有的参数和状态会被保存,当再次执行生成器,生成器会从挂起的地方往后接着执行,直到StopIteration。
3.send传入生成器,这是最简单的协程模型。
4.next()等价于send(None)