您的位置:首页 > 编程语言 > Python开发

python学习笔记-生成器,迭代器,协程定义

2017-11-11 23:05 781 查看

生成器

知识点:生成器就是通过封装的算法进行边循环边计算的机制,通过next()方法可以每次取一个计算的值,也就是yield b 这个b是多少(x = next(generator) 这个x就是取到的b)。x = yield,那么x 就是用于接收外部发来的值(generator.send(5),那么x就是5)。

通过列表生成式我们可以取得想要的值,但是如果我们创建了一个包含了百万个值的列表,而只要取前面的几个值,显然这造成了内存和时间的浪费。此时,如果我们能用某种方法来把我们想要的值循环计算出来,那么这无疑解决了上面的问题。这种通过某种算法进行边循环边计算的功能,称作生成器。

创建一个生成器的方法有

1.直接把列表推导式的[] 变为 ()

>>> g1 = (x for x in range(5))
>>> next(g1)    # 通过 next(g1) 或者 g1.send(None) 或者 g1,__next__() 启动生成器  (next(g1) == g1.send(None))
0
>>> next(g1)    # 再通过next(g1) g1.send(None)  就可以取得下一个值或者 g1.send(5)
1
>>> g1.send(4)
2
>>> list(g1)    # 一次性取出剩下的值
[3, 4]
>>> next(g1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration


当然,重新创建一个生成器g1,我们可以直接用for循环进行迭代取值

>>> g1 = (x for x in range(5))
>>> for i in g1:
...     print(i)
...
0
1
2
3
4


2.当需要计算一些复杂的值时(比如计算斐波那契数),用列表推导式无法实现。 这时候我们要用函数实现。

def fib(max):
n, a, b = 0, 0, 1
print('aaa')
while n < max:
print(b)
a, b = b, a + b
n = n + 1
print('a')
>>>fib(6)
aaa
1
1
2
3
5
8
a


我们说过生成器保存的是算法,如果这个算法封装在函数里,那我们如何将这个函数变成生成器呢? 将yield放在循环中就可以实现。yield可以接收外部发送的值(消费者生产者模式就是利用这个特性),还可以用于生成函数内部迭代的值

(1) yield用于生成函数内部迭代的值

def fib(max):
n, a, b = 0, 0, 1
print('aaa')
while n < max:
yield b
a, b = b, a + b
n = n + 1
print('a')

>>> g = fib(5)
>>> for i in g:
...     print(i)
...
aaa
1
1
2
3
5
a

函数的生成器也可以用next启动,取值
>>> g = fib(5)
>>> next(g)     # 解析:启动生成器,程序执行到yield处等待,接下来yield就可以用于接收值(下面会有生产者消费者模式)。以后每次next都会循环一次while语句
aaa
1
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> g.send(5)
5


(2)yield可以接收外部发送的值(生产者消费者模式)

def consumer(name):
weikou = 0
while weikou < 10:
egg = yield
print("%s 在吃 egg [%s]" % (name,egg))
weikou += 1

c = consumer('Tom')
next(c)    # 此处等待接收
c.send(1)    # Tom 在吃 egg [1]
c.send(2)    # Tom 在吃 egg [2]

def producer():
c1 = consumer("Tom")
c2 = consumer("John")
c1.__next__()
c2.send(None)
for i in range(3):
c1.send(i)
c2.send(i)

p = producer()
out-------------
Tom 在吃 egg [1]
Tom 在吃 egg [2]
Tom 在吃 egg [0]
John 在吃 egg [0]
Tom 在吃 egg [1]
John 在吃 egg [1]
Tom 在吃 egg [2]
John 在吃 egg [2]


上面这段代码是最简单的单线程下的并行的例子,也即协程,能同时进行多个任务。例子中就是2个消费者在同时吃包子,如果不用生成器的话,那么就需要等到一个人吃完了包子,另一个人再吃。

迭代
4000

用于for循环的数据类型有2类。

1.集合数据类型 list,dict,tuple,set,str…

2.generator。包括生成器和带yield的generator function

只要是可以用for循环,那他就是可迭代对象(Iterable)。

>>> from collections import Iterable
>>> isinstance([],Iterable)
True
>>> isinstance([1,2,3],Iterable)
True
>>> isinstance((),Iterable)
True
>>> isinstance({},Iterable)
True
>>> isinstance((x for x in range(10)),Iterable)
True


而像生成器这种还能用next()不断调用取值的就是迭代器。而集合数据类型的就不是迭代器

>>> from collections import Iterator
>>> isinstance([],Iterator)
False
>>> isinstance((x for x in range(10)),Iterator)
True


如果想把集合数据类型转为迭代器,可以用iter方法进行转换

>>> isinstance(iter([]),Iterator)
True


其实python的for循环本质上也是用next()调用的。

for i in [1,2,3,4,5]:
print(i)

#等价于

it = iter([1,2,3,4,5])

while True:
try:
x = next(it)
print(x)
except StopIteration as e:
break


协程定义

1.必须在只有一个单线程里实现并发

2.修改共享数据不需加锁

3.用户程序里自己保存多个控制流的上下文栈

4.一个协程遇到IO操作自动切换到其它协程

根据协程定义,上面的写的生产者消费者模型并不属于严格意义上的协程,因为并没有进行IO操作
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python