Python基礎學習之六yield

2021-08-28 05:07:17 字數 3973 閱讀 7278

協程的底層架構是在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 是函式名 中是用來放引數 形參和實參 的 之後的所有縮進行構成了函式體,指出了這個函式需要完成怎樣的工作 呼叫函式的時候,只需要傳遞相應引數和指出函式名...