Python协程的理解
1. Python中yield的理解
def call(k):
return k*3
def yield_test1(n):
for i in range(n):
z = yield call(i)
print("z:j=%s" % i)
n = yield_test1(10)
print(next(n))
print(n.send(11))
执行结果
0
z:j=0
3
Process finished with exit code 0
- 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator(生成器)。
- next(n)相当于send(None),每一执行next或send时,程序遇到yield的时候暂停,并将yield 同行的语句返回(有点类似return)。
- 第一次执行next(n)执行到yield call(i),返回结果0。第二次send(11)将执行,print(“z:j=%s” % i), yield call(i),返回结果打印z:j=0,返回值3。
- yield执行到最后抛出StopIteration错误表示无法继续返回下一个值了,本例中for语句自带捕获StopIteration异常,终止循环,其他的情况可能会需要进行异常处理。
- generator(生成器)一般没有return语句,如果任然要写return “123”语句,但是无法正常获得这个值,需要按照以下方法:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
# 捕获Generator的返回值
g = fib(6)
while True:
try:
x=next(g)
print('g=',x)
except StopIteration as e:
print('Generrator return value:', e.value)
break
执行获取结果:
g= 1
g= 1
g= 2
g= 3
g= 5
g= 8
Generrator return value: done
2. Python中yield from的理解
#输出斐波那契數列前 N 个数
def fab3(max):
n, a, b = 0, 0, 1
while n < max:
yield b
# print b
a, b = b, a + b
n = n + 1
def f_wrapper1(f):
for g in f:
yield g
wrap = f_wrapper1(fab3(5))
for i in wrap:
print(i,end=' ')
输出结果:
1 1 2 3 5
Process finished with exit code 0
使用yield from修改(使用yield from代替for循环)
#输出斐波那契數列前 N 个数
def fab3(max):
n, a, b = 0, 0, 1
while n < max:
yield b
# print b
a, b = b, a + b
n = n + 1
def f_wrapper2(f):
yield from f # 注意此处必须是一个可生成对象
wrap = f_wrapper2(fab3(5))
for i in wrap:
print(i, end=' ')
yield from iterable本质上等于for item in iterable: yield item的缩写版
print('yield from包含多个子程序')
def g(x):
yield from range(x, 0, -1)
yield from range(x)
print(list(g(5)))
for g in g(6):
print(g,end=',')
利用yield from语句向生成器(协程)传送数据
传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。
如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,换回生产者继续生产,效率极高:
import time
def consumer_work(len):
# 读取send传进的数据,并模拟进行处理数据
print("writer:")
w = ''
while True:
w = yield w # w接收send传进的数据,同时也是返回的数据
print('[CONSUMER] Consuming %s...>> ', w)
w *= len # 将返回的数据乘以100
time.sleep(0.1)
def consumer(coro):
yield from coro # 将数据传递到协程(生成器)对象中
def produce(c):
c.send(None) # "prime" the coroutine
for i in range(5):
print('[Produce] Producing %s----', i)
w = c.send(i) # 发送完成后进入协程中执行
print('[Produce] receive %s----', w)
c.close()
c1 = consumer_work(100)
produce(consumer(c1))
执行结果:
writer:
[Produce] Producing %s---- 0
[CONSUMER] Consuming %s...>> 0
[Produce] receive %s---- 0
[Produce] Producing %s---- 1
[CONSUMER] Consuming %s...>> 1
[Produce] receive %s---- 100
[Produce] Producing %s---- 2
[CONSUMER] Consuming %s...>> 2
[Produce] receive %s---- 200
[Produce] Producing %s---- 3
[CONSUMER] Consuming %s...>> 3
[Produce] receive %s---- 300
[Produce] Producing %s---- 4
[CONSUMER] Consuming %s...>> 4
[Produce] receive %s---- 400
Process finished with exit code 0