python生成器 协程
生成器进阶
看个例子:
def gg(): n='' i=0 while True: n=yield i #通过send传入到n if not n: pass else: print 'hehe',n i=100 #传进参数n的时候,使得i的值也变化了 i+=1 if __name__=='__main__': a=gg() print a.send(None) print a.next() print a.send(None) print a.send(9) print a.next() a.close()
结果:
/usr/bin/python2.7 /home/dahu/PycharmProjects/SpiderLearning/request_lianxi/t7.thread.4.py 0 1 2 hehe 9 101 102
可以看出
- 生成器的send(None)方法等于next()方法,next()方法可以直接替换成for循环
- 通过send(n)传递参数n进生成器,生成器里面通过yield关键字来获取参数的值
协程
我们利用这个特性,来学习一下协程
#!/usr/bin/python # coding=utf-8 # __author__='dahu' # data=2017- #多线程更改变量 import time def consumer(): r = '' while True: n = yield r if not n: return print('[CONSUMER] Consuming %s...' % n) time.sleep(0.5) r = '200 OK' def produce(c): c.next() n = 0 while n < 5: n = n + 1 print('[PRODUCER] Producing %s...' % n) r = c.send(n) #给生成器里传参,同时获取生成器里的值 print('[PRODUCER] Consumer return: %s' % r) c.close() if __name__=='__main__': c = consumer() produce(c)
结果,
/usr/bin/python2.7 /home/dahu/PycharmProjects/SpiderLearning/request_lianxi/t7.thread.4.py [PRODUCER] Producing 1... [CONSUMER] Consuming 1... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 2... [CONSUMER] Consuming 2... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 3... [CONSUMER] Consuming 3... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 4... [CONSUMER] Consuming 4... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 5... [CONSUMER] Consuming 5... [PRODUCER] Consumer return: 200 OK Process finished with exit code 0
注意到consumer函数是一个generator(生成器),把一个consumer传入produce后:
-
首先调用c.next()启动生成器;
-
然后,一旦生产了东西,通过c.send(n)切换到consumer执行;
-
consumer通过yield拿到消息,处理,又通过yield把结果传回;
-
produce拿到consumer处理的结果,继续生产下一条消息;
-
produce决定不生产了,通过c.close()关闭consumer,整个过程结束。
gevent
Python通过
yield
提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。gevent是第三方库,通过greenlet实现协程,其基本思想是:
当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成
直接看例子:
#!/usr/bin/python #coding=utf-8 #__author__='dahu' #data=2017- from gevent import monkey; monkey.patch_all() import gevent import urllib2 def f(url): print('GET: %s' % url) resp = urllib2.urlopen(url) data = resp.read() print('%d bytes received from %s.' % (len(data), url)) gevent.joinall([ gevent.spawn(f, 'https://www.cnblogs.com/dahu-daqing/'), gevent.spawn(f, 'https://www.liaoxuefeng.com/'), gevent.spawn(f, 'https://www.baidu.com/'), ])
结果: 从结果看,3个网络操作是并发执行的,而且结束顺序不同,但只有一个线程。
/usr/bin/python2.7 /home/dahu/PycharmProjects/SpiderLearning/request_lianxi/t10.gevent.py GET: https://www.cnblogs.com/dahu-daqing/ GET: https://www.liaoxuefeng.com/ GET: https://www.baidu.com/ 227 bytes received from https://www.baidu.com/. 38708 bytes received from https://www.liaoxuefeng.com/. 48289 bytes received from https://www.cnblogs.com/dahu-daqing/. Process finished with exit code 0