生成器函数的一个特点就是走走停停,代码执行到了yield语句就停下来了,直到下一次调用,再从yield继续执行,这个特性可以应用在编写python协程。协程,顾名思义,协作编程,跟多线程的表现形式,目的一样,区别在协程只有一个线程,在一个线程中的不同函数间切换,可以减少切换线程的开销,也可减少多线程的不安全性。而灵活利用generator就可以做到协程。
next()和send()都是调用生成器的方法,区别在于send()可以传入参数,看个例子:
1 def consumer():
2 r = ''
3 while True:
4 n = yield r
5 if not n:
6 return
7 print('[CONSUMER] Consuming %s...' % n)
8 r = '200 OK'
9
10 def produce(c):
11 c.send(None)
12 n = 0
13 while n < 5:
14 n = n + 1
15 print('[PRODUCER] Producing %s...' % n)
16 r = c.send(n)
17 print('[PRODUCER] Consumer return: %s' % r)
18 c.close()
c = consumer()
produce(c)
执行后的结果如下:
[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
一步一步看,首先consumer()是个生成器函数,因为里面有yield,先把这个生成器函数对象赋值给c,然后调用produce函数,c作为参数。先执行第11行,c.send(None)等同于next(c),先让generator走起来,等到了第四行yield语句就停,目的是下次调用send()可以传入参数,下一段会讲解。接着producer从第12行开始执行,看到这是不是很像多线程的程序?
然后,producer的n变为1打印出来,再传给consumer,这时候producer调用了send(),切换到了consumer第四行,同时consumer的n给赋值了1,producer的r等待一个函数返回值。观察打印结果,该返回值为'200 OK',就是consumer里面的r值。我们来看看是怎么返回的这个r。
consumer的执行过程是4 -> 5 -> 7 -> 8 -> 3 -> 4。 可见,第8行'200 OK'赋值给r之后,第4行由yield r返回,返回值就是yield后面的值,yield就像是个暂停键。
总结一下,yield有两个作用。1:作为“暂停键”,将程序挂起。2:返回yield后面表达式的值,类似于lambda函数的值返回形式。