生成器与迭代器的使用规则

Posted 风之岚翔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了生成器与迭代器的使用规则相关的知识,希望对你有一定的参考价值。

斐波拉契数列

技术分享图片
 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 
10 fib(10)
11 #注意赋值语句:a,b = b,a+b
12 
13 
14 a,b = b,a+b
15 1,1 = 1,0+1
16 1 ,2 = 1,1+1
17 2,3  =  2,1+2
18 3,5  = 3,2+3
19 5,8  = 5,3+5
20 8,13 = 8,5+8
21 13,21= 13,8+13
菲波那切数列

 

用函数变成生成器修改斐波拉契数列

技术分享图片
 1 #将print改为yeild:
 2 def fib(max):
 3     n,a,b = 0,0,1
 4     while n < max:
 5         #print(b)
 6         yield b
 7         a,b = b,a+b
 8         n = n + 1
 9     return done
10 
11 ret = fib(10)
12 print(ret)
13 
14 
15 #显示结果:
16 <generator object fib at 0x0000000000B387D8>
斐波那契数列生成器

 

生成器的调用

每执行一个ret.__next__(),就调用一次。


一、优点:

每一次调用就会中断退出,然后在调用在退出,以此类推。
例如:每调用一次,程序就会退出,然后在去做别的事儿,做完在用ret.__next__(),调用下一个生成器。

技术分享图片
 1 def fib(max):
 2     n,a,b = 0,0,1
 3     while n < max:
 4         #print(b)
 5         yield b
 6         a,b = b,a+b
 7         n = n + 1
 8     return done
 9 
10 ret = fib(10)
11 print(ret)
12 print(ret.__next__())
13 print(ret.__next__())
14 print(ret.__next__())
调用生成器实例

 

二、缺点:

__next__()调用超出迭代器的范围,所以报错。
生产中是不知道迭代器的元素有多少,所以只能等待异常的出现。

因此可以用异常处理来抓住生成器的结束,如下:

技术分享图片
 1 def fib(max):        1   6
 2     n,a,b = 0,0,1    7
 3     while n < max:   8  16
 4         #print(b)
 5         yield b       9  13  17 #中断退出,并将至返回给next()
 6         a,b = b,a+b  14
 7         n = n + 1    15
 8     return 异常
 9 
10 g = fib(6)       2
11 
12 while True:       3    11
13     try:          4
14         #内置方法,与__next__()是一样的,返回到上一次yield中断的地方
15         x = next(g)   5  12
16         print(g:,x)  10  18
17     except  StopIteration as  e:
18         print(Generator return values:,e.value)
19         break
生成器异常处理的执行顺序

 

协程

一、概念:cpu调用程序分为:进程--(包含多个)-->线程--(包含多个)-->协程

二、作用:通过生成器yield实现单线程的情况下实现并发运算效果(异步IO的雏形)。

三、工作原理:

1、生成器只有在调用时才会生成相应的数据
2、调用方式有 " str__next__.()   str.send() "3、并且每调用一次就产生一个值调用到最后一个值后会报错
4、报错可用try和except做异常处理
 
四、注意事项:
next:是直接调用yield,并不会传值。
send:是调用并直接传值给yield。

 

五、实例:

技术分享图片
 1 #!/usr/bin/env python
 2 # -*- coding:utf8 -*-
 3 # Author:Dong Ye
 4 
 5 
 6 ‘‘‘
 7 定义两个模型:
 8 一个是生产包子的。(生成器)
 9 另一个是吃包子的。(迭代器)
10 
11 这段功能实现了异步IO的雏形,也是一个简单的协程处理方式。
12 协程的特点:实际是串行方式分开执行的,但由于运行效果快,给人的感觉像是并行。
13 因此,协程也叫作:单线程下的并行执行效果。
14 协程是包含在线程里的一个单位,线程时进程的一个单位。
15 例如:enginx在异步单线程下,比多线程要快好多倍,也就是这种效果。
16 ‘‘‘
17 
18 import time
19 
20 
21 #吃包子的
22 def consumer(name):
23     print(%s 准备吃包子了! % name)
24     while True:
25         baozi = yield
26         print("包子[%s]来了。被[%s]吃了!" %(baozi,name))
27 
28 
29 #生产包子的
30 def producer(name):
31     #先定义2个协程(消费者)#将函数变成生成器
32     c1 = consumer(A)   #2个消费者
33     c2 = consumer(B)   #相当于2个协程(进程,线程,协程)
34     #开始调用生成器初始化(准备吃包子)
35     c1.__next__()     #开始调用生成器,只有next的时候才会到yield进行下一个操作
36     c2.__next__()
37     print(老子开始吃包子拉!)
38     #循环的次数,每次循环都会传值给生成器(产生什么样的包子)
39     for i in range(10):
40         time.sleep(1)
41         print("做了一个包子,分2半,一人一半")
42         c1.send(i) #包子的类型
43         c2.send(i)
44 
45 
46 producer("alex")
47 
48 
49 
50 ‘‘‘
51 #手动做包子:
52 c = consumer("dy")
53 c.__next__()
54 #c.__next__()
55 
56 b1 = "韭菜馅"
57 c.send(b1)     #调用+传值
58 #c.__next__()  #只调用,不传值
59 ‘‘‘
60 
61 
62 
63 显示结果:
64 A 准备吃包子了!
65 B 准备吃包子了!
66 老子开始吃包子拉!
67 做了一个包子,分2半,一人一半  #任务1
68 包子[0]来了。被[A]吃了!      #任务2
69 包子[0]来了。被[B]吃了!      #任务3
70 做了一个包子,分2半,一人一半
71 包子[1]来了。被[A]吃了!
72 包子[1]来了。被[B]吃了!
73 做了一个包子,分2半,一人一半
74 包子[2]来了。被[A]吃了!
75 包子[2]来了。被[B]吃了!
76 做了一个包子,分2半,一人一半
77 包子[3]来了。被[A]吃了!
78 包子[3]来了。被[B]吃了!
79 做了一个包子,分2半,一人一半
80 包子[4]来了。被[A]吃了!
81 包子[4]来了。被[B]吃了!
82 做了一个包子,分2半,一人一半
83 包子[5]来了。被[A]吃了!
84 包子[5]来了。被[B]吃了!
85 做了一个包子,分2半,一人一半
86 包子[6]来了。被[A]吃了!
87 包子[6]来了。被[B]吃了!
88 做了一个包子,分2半,一人一半
89 包子[7]来了。被[A]吃了!
90 包子[7]来了。被[B]吃了!
91 做了一个包子,分2半,一人一半
92 包子[8]来了。被[A]吃了!
93 包子[8]来了。被[B]吃了!
94 做了一个包子,分2半,一人一半
95 包子[9]来了。被[A]吃了!
96 包子[9]来了。被[B]吃了!
异步IO的雏形

 




以上是关于生成器与迭代器的使用规则的主要内容,如果未能解决你的问题,请参考以下文章

迭代器生成器与递归调用

python基础理解迭代器与生成器

python笔记-4(装饰器生成器迭代器)

python 生成器与迭代器(yield 用法)

处理数组和 ES6 迭代器的混合

生成器和迭代器的原理及使用