python的生成器和协程
2016-04-13
python的生成器和协程都是用yield
实现的
以下实践基于python2.7, 使用python3的小伙伴将print 和 next 调用改为python3的用法即可
生成器
在python中, 我们可以用列表表达式
生成一个list, 很方便, 如下:
m = [ x for x in range(0,10)]
print m #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
这样我们就能很简单的获取生成一个list了, 但假如, 我们需要生成的list对象很大, 那这种生成方式就可能撑爆内存了,就直接内存溢出了
这时候,我们就可以用生成器了, 假如我们需要打印100000000000000 以内的所有偶数, 如下
def ou_output():
c = 0
while c < 100000000000000:
if c % 2 == 0:
yield c
c += 1
m = out_output()
print m#<generator object ou_output at 0x7f21f71a2eb0>
这里我们得到的m, 并不是函数的返回结果, 而是一个生成器(generator)类型的对象, 他保存了yield
的上下文关系, 在next的时候计算出结果, 然后返回, 如下
m.next() #0
m.next() #2
m.next() #4
这样, 只是每次在需要的时候计算出结果返回, 就不会占用太多的内存了,不过这样也增加了cpu的计算压力。
##协程
python 进程并发处理的时候, 有几种方式,
多进程 多线程 协程
多进程和多线程就不说了, 比较常见, 协程的概念可能大多数人都不太熟悉
在上面的生成器的例子中, 我们看到包含yield
的function 都是生成器, 在执行到yield
的位置的时候, 返回了yield的结果,跳出生成器, 执行外面的逻辑, next(实际上还有一个send, 也有类似于next的功能, 还能像生成器内部传递动态参数)后又进入到生成器里面, 这样循环, 类似于这种中断执行, 协同处理的方式就是协程的实现,
举个例子, A和B两个人, A负责读取一个目录列表(比如人名),B 负责将内容写在黑板上, A读一个, B写一个。 这两个过程就就像是生产+消费者模式的一个简单的实例, producer A, consumer B。 传统的并发实现
用多进程的话, 进程通信不方便(当然,也是可以通过Queue等方式进行通信), 使用多线程编程的话, 会有死锁的问题, 如果用协程的方式来实现, 就很方便
如下
def writerB():
write_suc = False
while(True):
name = yield write_suc #send 进来的name, 注意, 这里的write_suc 会在下一个循环的时候yield, 作为到send的执行结果返回回去, 和上面的生成器例子里的yield c的 c 一样
print "write on blackbord: %s" %name
write_suc = True
time.sleep(1)
def readerA(wb):
names = [
"one fisher",
"Yi_Zhi_Yu",
"Tony Wang",
]
wb.next() #生成器运行前要先执行, 类似于开始执行
for name in names:
print "read name: %s" %name
write_suc = wb.send(name) #将name 发送到wb生成器的name, 进入wb中执行
wb.close()
if __name__ == "__main__":
wb = writerB()
readerA(wb)
结果如下:
read name: one fisher
write on blackbord: one fisher
read name: Yi_Zhi_Yu
write on blackbord: Yi_Zhi_Yu
read name: Tony Wang
write on blackbord: Tony Wang
在每次send的时候, readerA的执行就会被中断, 进入writerB里执行了, 再到下一个yield的时候返回到readerA中继续执行, 这样, A读-B写 – A读-B写 – A读-B写 整个执行单线程无锁, reader和writer协同实现