協程的底層架構是在pep342 中定義,並在python2.5 實現的。
python2.5 中,yield關鍵字可以在表示式中使用,而且生成器api中增加了 .send(value)方法。生成器可以使用.send(...)方法傳送資料,傳送的資料會成為生成器函式中yield表示式的值。
協程是指乙個過程,這個過程與呼叫方協作,產出有呼叫方提供的值。因此,生成器可以作為協程使用。
除了 .send(...)方法,pep342 和新增了 .throw(...)(讓呼叫方丟擲異常,在生成器中處理)和.close()(終止生成器)方法。
python3.3後,pep380對生成器函式做了兩處改動:
生成器可以返回乙個值;以前,如果生成器中給return語句提供值,會丟擲syntaxerror異常。
引入yield from 語法,使用它可以把複雜的生成器重構成小型的巢狀生成器,省去之前把生成器的工作委託給子生成器所需的大量模板**。
首先說明一下,協程有四個狀態,可以使用inspect.getgeneratorstate(...)函式確定:
gen_created # 等待開始執行
gen_running # 直譯器正在執行(只有在多執行緒應用中才能看到這個狀態)
gen_suspended # 在yield表示式處暫停
gen_closed # 執行結束
#! -*- coding: utf-8 -*-
import
inspect
# 協程使用生成器函式定義:定義體中有yield關鍵字。
def******_coroutine
():
print(
'-> coroutine started'
)
# yield 在表示式中使用;如果協程只需要從客戶那裡接收資料,yield關鍵字右邊不需要加表示式(yield預設返回none)
x =
yield
print(
'-> coroutine received:'
, x)
my_coro = ******_coroutine()
# 和建立生成器的方式一樣,呼叫函式得到生成器物件。
print(inspect.getgeneratorstate(my_coro))
# 協程處於 gen_created (等待開始狀態)
my_coro.send(
none
)# 首先要呼叫next()函式,因為生成器還沒有啟動,沒有在yield語句處暫停,所以開始無法傳送資料
# 傳送 none 可以達到相同的效果 my_coro.send(none)
next(my_coro)
# 此時協程處於 gen_suspended (在yield表示式處暫停)
print(inspect.getgeneratorstate(my_coro))
# 呼叫這個方法後,協程定義體中的yield表示式會計算出42;現在協程會恢復,一直執行到下乙個yield表示式,或者終止。
my_coro.send(42)
print(inspect.getgeneratorstate(my_coro))
執行上述**,輸出結果如下
gen_created
-> coroutine started
gen_suspended
-> coroutine received:
42# 這裡,控制權流動到協程定義體的尾部,導致生成器像往常一樣丟擲stopiteration異常
traceback (most recent call last):
file
"/users/gs/coroutine.py"
, line
18, in
my_coro.send(42)
stopiteration
send方法的引數會成為暫停yield表示式的值,所以,僅當協程處於暫停狀態是才能呼叫send方法。
如果協程還未啟用(gen_created 狀態)要呼叫next(my_coro) 啟用協程,也可以呼叫my_coro.send(none)
如果建立協程物件後立即把none之外的值發給它,會出現下述錯誤:
>>>
my_coro = ******_coroutine()
>>>
my_coro.send(
123)
traceback (most recent call last):
file
"/users/gs/coroutine.py"
, line
14, in
my_coro.send(
123)
typeerror: can
't send non-none value to a just-started generator
仔細看錯誤訊息
can't send non-none value to a just-started generator
最先呼叫next(my_coro) 這一步通常稱為」預激「(prime)協程---即,讓協程向前執行到第乙個yield表示式,準備好作為活躍的協程使用。
再看乙個兩個值得協程
def******_coro2
(a):
print(
'-> coroutine started: a ='
, a)
b =
yielda
print(
'-> received: b ='
, b)
c =
yield
a + b
print(
'-> received: c ='
, c)
my_coro2 = ******_coro2(14)
print(inspect.getgeneratorstate(my_coro2))
# 這裡inspect.getgeneratorstate(my_coro2) 得到結果為 gen_created (協程未啟動)
next(my_coro2)
# 向前執行到第乙個yield 處 列印 「-> coroutine started: a = 14」
# 並且產生值 14 (yield a 執行 等待為b賦值)
print(inspect.getgeneratorstate(my_coro2))
# 這裡inspect.getgeneratorstate(my_coro2) 得到結果為 gen_suspended (協程處於暫停狀態)
my_coro2.send(28)
# 向前執行到第二個yield 處 列印 「-> received: b = 28」
# 並且產生值 a + b = 42(yield a + b 執行 得到結果42 等待為c賦值)
print(inspect.getgeneratorstate(my_coro2))
# 這裡inspect.getgeneratorstate(my_coro2) 得到結果為 gen_suspended (協程處於暫停狀態)
my_coro2.send(99)
# 把數字99傳送給暫停協程,計算yield 表示式,得到99,然後把那個數賦值給c 列印 「-> received: c = 99」
# 協程終止,丟擲stopiteration
執行上述**,輸出結果如下
gen_created
-> coroutine started: a =
14gen_suspended
-> received: b =
28-> received: c =
99traceback (most recent call last):
file
"/users/gs/coroutine.py"
, line
37, in
my_coro2.send(99)
stopiteration
python基礎六之編碼
python中編碼的特點 1,各個編碼之間的二進位制是不能互相識別的,會產生亂碼 2,檔案的儲存和傳輸是不能用unicode的 在python3中字串在記憶體中是用unicode編碼的,而要完成儲存和傳輸則要將str轉換為bytes型別。bytes的操作方式和str相同,但是它的編碼和str不同,可...
python基礎學習六 模組
一 模組匯入 模組的匯入方式 最後一種方法,意味著匯入一切非私有的物件,但是會存在名稱衝突的情況,或者如果模組有乙個全域性的 all 變數,其中存放乙個名稱列表,就匯入名稱包含在 all 變數中的所有物件。為了避免衝突,很多指南中規定只能使用import importable方式。通常自定義模組名的...
Python的基礎學習 六 函式
4.傳遞實參的應用 5.將函式儲存到模組中 def hello print hello world hello def是用來定義函式的 hello 是函式名 中是用來放引數 形參和實參 的 之後的所有縮進行構成了函式體,指出了這個函式需要完成怎樣的工作 呼叫函式的時候,只需要傳遞相應引數和指出函式名...