python生成器 协程

生成器

参考文章:  协程   gevent

生成器进阶

看个例子:

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
View Code

注意到consumer函数是一个generator(生成器),把一个consumer传入produce后:

  1. 首先调用c.next()启动生成器;

  2. 然后,一旦生产了东西,通过c.send(n)切换到consumer执行;

  3. consumer通过yield拿到消息,处理,又通过yield把结果传回;

  4. produce拿到consumer处理的结果,继续生产下一条消息;

  5. 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

 

posted @ 2017-08-22 20:46  dahu1  Views(232)  Comments(0Edit  收藏  举报