學習Python很多書籍都或多或少介紹協(xié)程,但是很少有人會在開發(fā)中使用握巢,一是理解不深晕鹊,使用怕出錯,二是有其他方式可以替代暴浦,知識代碼多一點溅话,便于理解啊,總之:不使用的理由千千種歌焦。
今天我就來和大家聊聊Python協(xié)程
協(xié)程是啥
協(xié)程在實際的應用
協(xié)程的定義
協(xié)程 是為非搶占式多任務產(chǎn)生子程序的計算機程序組件公荧,協(xié)程允許不同入口點在不同位置暫停或開始執(zhí)行程序”同规。從技術的角度來說循狰,“協(xié)程就是你可以暫停執(zhí)行的函數(shù)”
與生成器類似窟社,但yield
出現(xiàn)在表達式的右邊,且產(chǎn)出一個表達式值作為返回值绪钥,如果yield
后沒有表達式灿里,則返回None
。協(xié)程可以從調(diào)用方接收數(shù)據(jù)程腹,caller.send(params)匣吊。
1. 例子
>>> def simple(a):
print "start:a = ", (a)
b = yield a
print "received b= ",(b)
c = yield a + b
print "received :d ", (c)
>>> m1 = simple(2)
>>> m1
<generator object simple at 0x0000000003D830D8>
>>> next(m1) # 激活協(xié)程, 并產(chǎn)出a,然后暫停
start:a = 2
2
>>> m1.send(3)
received b= 3
5
>>> m1.send(1000) # b 接收數(shù)據(jù)寸潦,并產(chǎn)出 a+b 的結(jié)果色鸳,然后暫停
received :d 1000
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
m1.send(1000)
StopIteration
把yield
作為控制流程的方式來理解。
上圖中 第一階段 yield a
結(jié)束见转,第二階段是給b
賦值開始
再看一個例子:
def averager():
total = 0
count = 0
avg = None
while True:
term = yield avg
total += term
count +=1
avg = total/count
ag = averager()
next(ag) # 運行到 yield avg 就停止命雀,并返回avg值 None
ag.send(100) # item 接收到100,并運行,直到下一個yield 并停止
2. 與協(xié)程相關的方法
我們知道了怎么創(chuàng)建協(xié)程斩箫,但是當執(zhí)行到最后吏砂,如果不處理就會和生成器一樣拋出一個stopInteration
異常來終止該協(xié)程,從2.5版本開始添加了兩個方法throw
和close
來終止協(xié)程
如果generator.thow(exception)的異常被捕獲乘客,則繼續(xù)下一個yield,否則終止
3. 協(xié)程返回值
在定義體中 return value
from collections import namedTuple
Result = namedTuple("Result","count average")
def average():
total = 0
count = 0
avg = None
while True:
term = yield
if term is None:
break # 終止符
total +=term
count +=1
avg = total/count
return Result(count,avg)
當send(None)
就會break, 返回狐血。其中Result 會作為異常的value屬性返回
try:
m2 .send(None)
exception StopInteration as exc:
print exc.value
應用
# BEGIN YIELD_FROM_AVERAGER
from collections import namedtuple
Result = namedtuple('Result', 'count average')
# the subgenerator
def averager(): # <1>
total = 0.0
count = 0
average = None
while True:
term = yield # <2>
if term is None: # <3>
break
total += term
count += 1
average = total/count
return Result(count, average) # <4>
# the delegating generator
def grouper(results, key): # <5>
while True: # <6>
results[key] = yield from averager() #生成一個使用協(xié)程的生成器,在此處暫停易核,等 將一個Result 賦值給對應的key
# the client code, a.k.a. the caller
def main(data): # <8>
results = {}
for key, values in data.items():
group = grouper(results, key) # group是grouper函數(shù)生成的生成器對象
next(group) # 預激活協(xié)程
for value in values:
# 把各個 value 傳給 grouper匈织。傳入的值最終到達 averager 函數(shù)中 term = yield 那一行; grouper 永遠不知道傳入的值是什么
group.send(value)
# 內(nèi)層循環(huán)結(jié)束后牡直, group 實例依舊在 yield from 表達式處暫停报亩,因此, grouper函數(shù)定義體中為 results[key] 賦值的語句還沒有執(zhí)行
#把 None 傳入 grouper井氢,導致當前的 averager 實例終止,也讓 grouper 繼續(xù)運行岳链,再創(chuàng)建一個 averager 實例花竞,處理下一組值
group.send(None)
# print(results) # uncomment to debug
report(results)
# output report
def report(results):
for key, result in sorted(results.items()):
group, unit = key.split(';')
print('{:2} {:5} averaging {:.2f}{}'.format(
result.count, group, result.average, unit))
data = {
'girls;kg':
[40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
'girls;m':
[1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
'boys;kg':
[39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
'boys;m':
[1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}
if __name__ == '__main__':
main(data)
上述例子,如果子生成器不停止掸哑,委派生成器永遠在yield from
處停止约急。
yield from iterable
本質(zhì)上等于for item in iterable: yield item
的縮寫
yield from
的意義
把迭代器當作生成器使用,相當于把子生成器的定義體內(nèi)聯(lián)在 yield from 表達式
中苗分。此外厌蔽,子生成器可以執(zhí)行 return 語句,返回一個值摔癣,而返回的值會成為 yield
from 表達式的值
子生成器產(chǎn)出的值都直接傳給委派生成器的調(diào)用方(即客戶端代碼將上述例子的averager 直接給了main中的group)奴饮。
使用
send()
方法發(fā)給委派生成器的值都直接傳給子生成器纬向。如果發(fā)送的值是
None
,那么會調(diào)用子生成器的__next__()
方法戴卜。如果發(fā)送的值不是None
逾条,那么會
調(diào)用子生成器的send()
方法。如果調(diào)用的方法拋出StopIteration 異常
投剥,那么委
派生成器恢復運行师脂。任何其他異常都會向上冒泡,傳給委派生成器江锨。生成器退出時吃警,生成器(或子生成器)中的
return expr
表達式會觸發(fā)
StopIteration(expr)
異常拋出。yield from
表達式的值是子生成器終止時傳給StopIteration
異常的第一個參
數(shù)啄育。
** yield from
結(jié)構的另外兩個特性與異常和終止有關酌心。**
- 傳入委派生成器的異常,除了
GeneratorExit
之外都傳給子生成器的throw()
方
法灸撰。如果調(diào)用throw()
方法時拋出StopIteration
異常谒府,委派生成器恢復運
行。StopIteration
之外的異常會向上冒泡浮毯,傳給委派生成器完疫。 - 如果把
GeneratorExit
異常傳入委派生成器,或者在委派生成器上調(diào)用close()
方
法债蓝,那么在子生成器上調(diào)用close()
方法壳鹤,如果它有的話。如果調(diào)用close()
方法
導致異常拋出饰迹,那么異常會向上冒泡芳誓,傳給委派生成器;否則啊鸭,委派生成器拋出
GeneratorExit
異常锹淌。