python協(xié)程函數(shù) python協(xié)方差函數(shù)

Python異步編程4:協(xié)程函數(shù),協(xié)程對(duì)象,await關(guān)鍵字

協(xié)程函數(shù):async def?函數(shù)名。3.5+

成都一家集口碑和實(shí)力的網(wǎng)站建設(shè)服務(wù)商,擁有專(zhuān)業(yè)的企業(yè)建站團(tuán)隊(duì)和靠譜的建站技術(shù),十余年企業(yè)及個(gè)人網(wǎng)站建設(shè)經(jīng)驗(yàn) ,為成都上千家客戶提供網(wǎng)頁(yè)設(shè)計(jì)制作,網(wǎng)站開(kāi)發(fā),企業(yè)網(wǎng)站制作建設(shè)等服務(wù),包括成都營(yíng)銷(xiāo)型網(wǎng)站建設(shè),品牌網(wǎng)站制作,同時(shí)也為不同行業(yè)的客戶提供網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)的服務(wù),包括成都電商型網(wǎng)站制作建設(shè),裝修行業(yè)網(wǎng)站制作建設(shè),傳統(tǒng)機(jī)械行業(yè)網(wǎng)站建設(shè),傳統(tǒng)農(nóng)業(yè)行業(yè)網(wǎng)站制作建設(shè)。在成都做網(wǎng)站,選網(wǎng)站制作建設(shè)服務(wù)商就選創(chuàng)新互聯(lián)建站

協(xié)程對(duì)象:執(zhí)行協(xié)程函數(shù)()得到的協(xié)程對(duì)象。

3.5之后的寫(xiě)法:

3.7之后的寫(xiě)法:更簡(jiǎn)便

await后面?跟?可等待的對(duì)象。(協(xié)程對(duì)象,F(xiàn)uture,Task對(duì)象?約等于IO等待)

await實(shí)例2:串行執(zhí)行。 一個(gè)協(xié)程函數(shù)里面可以支持多個(gè)await ,雖然會(huì)串行,但是如果有其他協(xié)程函數(shù),任務(wù)列表也在執(zhí)行,依然會(huì)切換。只是案例中的main對(duì)應(yīng)執(zhí)行的others1和others2串行 。 await會(huì)等待對(duì)象的值得到之后才繼續(xù)往下走。

Python協(xié)程之a(chǎn)syncio

asyncio 是 Python 中的異步IO庫(kù),用來(lái)編寫(xiě)并發(fā)協(xié)程,適用于IO阻塞且需要大量并發(fā)的場(chǎng)景,例如爬蟲(chóng)、文件讀寫(xiě)。

asyncio 在 Python3.4 被引入,經(jīng)過(guò)幾個(gè)版本的迭代,特性、語(yǔ)法糖均有了不同程度的改進(jìn),這也使得不同版本的 Python 在 asyncio 的用法上各不相同,顯得有些雜亂,以前使用的時(shí)候也是本著能用就行的原則,在寫(xiě)法上走了一些彎路,現(xiàn)在對(duì) Python3.7+ 和 Python3.6 中 asyncio 的用法做一個(gè)梳理,以便以后能更好的使用。

協(xié)程,又稱(chēng)微線程,它不被操作系統(tǒng)內(nèi)核所管理,而完全是由程序控制,協(xié)程切換花銷(xiāo)小,因而有更高的性能。

協(xié)程可以比作子程序,不同的是,執(zhí)行過(guò)程中協(xié)程可以掛起當(dāng)前狀態(tài),轉(zhuǎn)而執(zhí)行其他協(xié)程,在適當(dāng)?shù)臅r(shí)候返回來(lái)接著執(zhí)行,協(xié)程間的切換不需要涉及任何系統(tǒng)調(diào)用或任何阻塞調(diào)用,完全由協(xié)程調(diào)度器進(jìn)行調(diào)度。

Python 中以 asyncio 為依賴,使用 async/await 語(yǔ)法進(jìn)行協(xié)程的創(chuàng)建和使用,如下 async 語(yǔ)法創(chuàng)建一個(gè)協(xié)程函數(shù):

在協(xié)程中除了普通函數(shù)的功能外最主要的作用就是:使用 await 語(yǔ)法等待另一個(gè)協(xié)程結(jié)束,這將掛起當(dāng)前協(xié)程,直到另一個(gè)協(xié)程產(chǎn)生結(jié)果再繼續(xù)執(zhí)行:

asyncio.sleep() 是 asyncio 包內(nèi)置的協(xié)程函數(shù),這里模擬耗時(shí)的IO操作,上面這個(gè)協(xié)程執(zhí)行到這一句會(huì)掛起當(dāng)前協(xié)程而去執(zhí)行其他協(xié)程,直到sleep結(jié)束,當(dāng)有多個(gè)協(xié)程任務(wù)時(shí),這種切換會(huì)讓它們的IO操作并行處理。

注意,執(zhí)行一個(gè)協(xié)程函數(shù)并不會(huì)真正的運(yùn)行它,而是會(huì)返回一個(gè)協(xié)程對(duì)象,要使協(xié)程真正的運(yùn)行,需要將它們加入到事件循環(huán)中運(yùn)行,官方建議 asyncio 程序應(yīng)當(dāng)有一個(gè)主入口協(xié)程,用來(lái)管理所有其他的協(xié)程任務(wù):

在 Python3.7+ 中,運(yùn)行這個(gè) asyncio 程序只需要一句: asyncio.run(main()) ,而在 Python3.6 中,需要手動(dòng)獲取事件循環(huán)并加入?yún)f(xié)程任務(wù):

事件循環(huán)就是一個(gè)循環(huán)隊(duì)列,對(duì)其中的協(xié)程進(jìn)行調(diào)度執(zhí)行,當(dāng)把一個(gè)協(xié)程加入循環(huán),這個(gè)協(xié)程創(chuàng)建的其他協(xié)程都會(huì)自動(dòng)加入到當(dāng)前事件循環(huán)中。

其實(shí)協(xié)程對(duì)象也不是直接運(yùn)行,而是被封裝成一個(gè)個(gè)待執(zhí)行的 Task ,大多數(shù)情況下 asyncio 會(huì)幫我們進(jìn)行封裝,我們也可以提前自行封裝 Task 來(lái)獲得對(duì)協(xié)程更多的控制權(quán),注意,封裝 Task 需要 當(dāng)前線程有正在運(yùn)行的事件循環(huán) ,否則將引 RuntimeError,這也就是官方建議使用主入口協(xié)程的原因,如果在主入口協(xié)程之外創(chuàng)建任務(wù)就需要先手動(dòng)獲取事件循環(huán)然后使用底層方法 loop.create_task() ,而在主入口協(xié)程之內(nèi)是一定有正在運(yùn)行的循環(huán)的。任務(wù)創(chuàng)建后便有了狀態(tài),可以查看運(yùn)行情況,查看結(jié)果,取消任務(wù)等:

asyncio.create_task() 是 Python3.7 加入的高層級(jí)API,在 Python3.6,需要使用低層級(jí)API asyncio.ensure_future() 來(lái)創(chuàng)建 Future,F(xiàn)uture 也是一個(gè)管理協(xié)程運(yùn)行狀態(tài)的對(duì)象,與 Task 沒(méi)有本質(zhì)上的區(qū)別。

通常,一個(gè)含有一系列并發(fā)協(xié)程的程序?qū)懛ㄈ缦拢≒ython3.7+):

并發(fā)運(yùn)行多個(gè)協(xié)程任務(wù)的關(guān)鍵就是 asyncio.gather(*tasks) ,它接受多個(gè)協(xié)程任務(wù)并將它們加入到事件循環(huán),所有任務(wù)都運(yùn)行完成后會(huì)返回結(jié)果列表,這里我們也沒(méi)有手動(dòng)封裝 Task,因?yàn)?gather 函數(shù)會(huì)自動(dòng)封裝。

并發(fā)運(yùn)行還有另一個(gè)方法 asyncio.wait(tasks) ,它們的區(qū)別是:

python協(xié)程(4):asyncio

asyncio是官方提供的協(xié)程的類(lèi)庫(kù),從python3.4開(kāi)始支持該模塊

async awiat是python3.5中引入的關(guān)鍵字,使用async關(guān)鍵字可以將一個(gè)函數(shù)定義為協(xié)程函數(shù),使用awiat關(guān)鍵字可以在遇到IO的時(shí)候掛起當(dāng)前協(xié)程(也就是任務(wù)),去執(zhí)行其他協(xié)程。

await + 可等待的對(duì)象(協(xié)程對(duì)象、Future對(duì)象、Task對(duì)象 - IO等待)

注意:在python3.4中是通過(guò)asyncio裝飾器定義協(xié)程,在python3.8中已經(jīng)移除了asyncio裝飾器。

事件循環(huán),可以把他當(dāng)做是一個(gè)while循環(huán),這個(gè)while循環(huán)在周期性的運(yùn)行并執(zhí)行一些協(xié)程(任務(wù)),在特定條件下終止循環(huán)。

loop = asyncio.get_event_loop():生成一個(gè)事件循環(huán)

loop.run_until_complete(任務(wù)):將任務(wù)放到事件循環(huán)

Tasks用于并發(fā)調(diào)度協(xié)程,通過(guò)asyncio.create_task(協(xié)程對(duì)象)的方式創(chuàng)建Task對(duì)象,這樣可以讓協(xié)程加入事件循環(huán)中等待被調(diào)度執(zhí)行。除了使用 asyncio.create_task() 函數(shù)以外,還可以用低層級(jí)的 loop.create_task() 或 ensure_future() 函數(shù)。不建議手動(dòng)實(shí)例化 Task 對(duì)象。

本質(zhì)上是將協(xié)程對(duì)象封裝成task對(duì)象,并將協(xié)程立即加入事件循環(huán),同時(shí)追蹤協(xié)程的狀態(tài)。

注意:asyncio.create_task() 函數(shù)在 Python 3.7 中被加入。在 Python 3.7 之前,可以改用 asyncio.ensure_future() 函數(shù)。

下面結(jié)合async awiat、事件循環(huán)和Task看一個(gè)示例

示例一:

*注意:python 3.7以后增加了asyncio.run(協(xié)程對(duì)象),效果等同于loop = asyncio.get_event_loop(),loop.run_until_complete(協(xié)程對(duì)象) *

示例二:

注意:asyncio.wait 源碼內(nèi)部會(huì)對(duì)列表中的每個(gè)協(xié)程執(zhí)行ensure_future從而封裝為T(mén)ask對(duì)象,所以在和wait配合使用時(shí)task_list的值為[func(),func()] 也是可以的。

示例三:

在Python中使用Asyncio系統(tǒng)(3-4)Task 和 Future

Task 和 Future

前面我們討論了協(xié)程,以及如何在循環(huán)中運(yùn)行它們才有用?,F(xiàn)在我想簡(jiǎn)單談?wù)凾ask和Future api。你將使用最多的是Task,因?yàn)槟愕拇蟛糠止ぷ鲗⑸婕笆褂胏reate_task()函數(shù)運(yùn)行協(xié)程,就像在第22頁(yè)的“快速開(kāi)始”中設(shè)置的那樣。Future類(lèi)實(shí)際上是Task的超類(lèi),它提供了與循環(huán)交互操作的所有功能。

可以這樣簡(jiǎn)單地理解:Future表示某個(gè)活動(dòng)的未來(lái)完成狀態(tài),并由循環(huán)管理。Task是完全相同的,但是具體的“activity”是一個(gè)協(xié)程——可能是你用async def函數(shù)加上create_task()創(chuàng)建的協(xié)程。

Future類(lèi)表示與循環(huán)交互的某個(gè)東西的狀態(tài)。這個(gè)描述太模糊了,不太有用,所以你可以將Future實(shí)例視為一個(gè)切換器,一個(gè)完成狀態(tài)的切換器。當(dāng)創(chuàng)建Future實(shí)例時(shí),切換設(shè)置為“尚未完成”狀態(tài),但稍后它將是“完成”狀態(tài)。事實(shí)上,F(xiàn)uture實(shí)例有一個(gè)名為done()的方法,它允許你檢查狀態(tài),如示例 3-15所示。

示例 3-15. 用done()方法檢查完成狀態(tài)

Future實(shí)例還可以執(zhí)行以下操作:

? 設(shè)置一個(gè)result值(用.set_result(value)設(shè)置值并且使用 .result()獲取值)

? 使用.cancel()方法取消 (并且會(huì)用使用.cancelled()檢查是否取消)

? 增加一個(gè)Future完成時(shí)回調(diào)的函數(shù)

即使Task更常見(jiàn),也不可能完全避免使用Future:例如,在執(zhí)行器上運(yùn)行函數(shù)將返回Future實(shí)例,而不是Task。讓我們快速看一下 示例 3-16 ,了解一下直接使用Future實(shí)例是什么感覺(jué)。

示例 3-16. 與Future實(shí)例的交互

(L3)創(chuàng)建一個(gè)簡(jiǎn)單的 main函數(shù)。我們運(yùn)行這個(gè)函數(shù),等上一會(huì)兒然后在Future f上設(shè)置一個(gè)結(jié)果。

(L5)設(shè)置一個(gè)結(jié)果。

(L8)手動(dòng)創(chuàng)建一個(gè)Future實(shí)例。注意,這個(gè)實(shí)例(默認(rèn)情況下)綁定到我們的循環(huán),但它沒(méi)有也不會(huì)被附加到任何協(xié)程(這就是Tasks的作用)。

(L9)在做任何事情之前,確認(rèn)future還沒(méi)有完成。

(L11)安排main()協(xié)程,傳遞future。請(qǐng)記住,main()協(xié)程所做的所有工作就是sleep,然后切換Future實(shí)例。(注意main()協(xié)程還不會(huì)開(kāi)始運(yùn)行:協(xié)程只在事件循環(huán)運(yùn)行時(shí)才開(kāi)始運(yùn)行。)

(L13)在這里我們?cè)贔uture實(shí)例上而不是Task實(shí)例上使用run_until_complete()。這和你以前見(jiàn)過(guò)的不一樣?,F(xiàn)在循環(huán)正在運(yùn)行,main()協(xié)程將開(kāi)始執(zhí)行.

(L16)最終,當(dāng)future的結(jié)果被設(shè)置時(shí),它就完成了。完成后,可以訪問(wèn)結(jié)果。

當(dāng)然,你不太可能以這里所示的方式直接使用Future;代碼示例僅用于教育目的。你與asynccio的大部分聯(lián)系都是通過(guò)Task實(shí)例進(jìn)行的。

你可能想知道如果在Task實(shí)例上調(diào)用set_result()會(huì)發(fā)生什么。在Python 3.8之前可以這樣做,但現(xiàn)在不允許這么做了。任務(wù)實(shí)例是協(xié)程對(duì)象的包裝器,它們的結(jié)果值只能在內(nèi)部設(shè)置為底層協(xié)程函數(shù)的結(jié)果,如 示例 3-17所示那樣。

示例 3-17. 在task上調(diào)用set_result

(L13)唯一的區(qū)別是我們創(chuàng)建的是Task實(shí)例而不是Future實(shí)例。當(dāng)然,Task API要求我們提供一個(gè)協(xié)程;這里我們使用sleep()只是因?yàn)楹?jiǎn)單方便。

(L7)正在傳入一個(gè)Task實(shí)例。它滿足函數(shù)的類(lèi)型簽名(因?yàn)門(mén)ask是Future的子類(lèi)),但從Python 3.8開(kāi)始,我們不再允許在Task上調(diào)用set_result():嘗試這樣做將引發(fā)RuntimeError。這個(gè)想法是,一個(gè)Task代表一個(gè)正在運(yùn)行的協(xié)程,所以結(jié)果應(yīng)該總是來(lái)自于task自身。

(L10, L24)但是,我們?nèi)匀豢梢詂ancel()一個(gè)任務(wù),它將在底層協(xié)程中引發(fā)CancelledError。

Create_task? Ensure_Future? 下定決心吧!

在第22頁(yè)的“快速入門(mén)”中,我說(shuō)過(guò)運(yùn)行協(xié)程的方法是使用asyncio.create_task()。在引入該函數(shù)之前,有必要獲取一個(gè)循環(huán)實(shí)例并使用loop.create_task()完成相同的任務(wù)。事實(shí)上,這也可以通過(guò)一個(gè)不同的模塊級(jí)函數(shù)來(lái)實(shí)現(xiàn):asyncio.ensure_future()。一些開(kāi)發(fā)人員推薦create_task(),而其他人推薦ensure_future()。

在我為這本書(shū)做研究的過(guò)程中,我確信API方法asyncio.ensure_future()是引起對(duì)asyncio庫(kù)廣泛誤解的罪魁禍?zhǔn)?。API的大部分內(nèi)容都非常清晰,但在學(xué)習(xí)過(guò)程中還存在一些嚴(yán)重的障礙,這就是其中之一。當(dāng)你遇到ensure_future()時(shí),你的大腦會(huì)非常努力地將其集成到關(guān)于asyncio應(yīng)該如何使用的心理模型中——但很可能會(huì)失敗!

在Python 3.6 asyncio 文檔中,這個(gè)現(xiàn)在已經(jīng)臭名昭著的解釋突出了 ensure_future() 的問(wèn)題:

asyncio.ensure_future(coro_or_future, *, _loop =None)

安排執(zhí)行一個(gè)協(xié)程對(duì)象:把它包裝在future中。返回一個(gè)Task對(duì)象。如果參數(shù)是Future,則直接返回。

什么!? 當(dāng)我第一次讀到這篇文章時(shí),我很困惑。下面希望是對(duì)ensure_future()的更清楚的描述:

這個(gè)函數(shù)很好地說(shuō)明了針對(duì)終端用戶開(kāi)發(fā)人員的asyncio API(高級(jí)API)和針對(duì)框架設(shè)計(jì)人員的asyncio API(低級(jí)API)之間的區(qū)別。讓我們?cè)谑纠?3-18中自習(xí)看看它是如何工作的。

示例 3-18. 仔細(xì)看看ensure_future()在做什么

(L3)一個(gè)簡(jiǎn)單的什么都不做的協(xié)程函數(shù)。我們只需要一些能組成協(xié)程的東西。

(L6)我們通過(guò)直接調(diào)用該函數(shù)來(lái)創(chuàng)建協(xié)程對(duì)象。你的代碼很少會(huì)這樣做,但我想在這里明確地表示,我們正在向每個(gè)create_task()和ensure_future()傳遞一個(gè)協(xié)程對(duì)象。

(L7)獲取一個(gè)循環(huán)。

(L9)首先,我們使用loop.create_task()在循環(huán)中調(diào)度協(xié)程,并返回一個(gè)新的Task實(shí)例。

(L10)驗(yàn)證類(lèi)型。到目前為止,沒(méi)有什么有趣的。

(L12)我們展示了asyncio.ensure_future()可以被用來(lái)執(zhí)行與create_task()相同的動(dòng)作:我們傳入了一個(gè)協(xié)程,并返回了一個(gè)Task實(shí)例(并且協(xié)程已經(jīng)被安排在循環(huán)中運(yùn)行)!如果傳入的是協(xié)程,那么loop.create_task()和asyncio.ensure_future()之間沒(méi)有區(qū)別。

(L15)如果我們給ensure_future()傳遞一個(gè)Task實(shí)例會(huì)發(fā)生什么呢?注意我們要傳遞的Task實(shí)例是已經(jīng)在第4步通過(guò)loop.create_task()創(chuàng)建好的。

(L16)返回的Task實(shí)例與傳入的Task實(shí)例完全相同:它在被傳遞時(shí)沒(méi)有被改變。

直接傳遞Future實(shí)例的意義何在?為什么用同一個(gè)函數(shù)做兩件不同的事情?答案是,ensure_future()的目的是讓框架作者向最終用戶開(kāi)發(fā)者提供可以處理兩種參數(shù)的API。不相信我?這是ex-BDFL自己說(shuō)的:

ensure_future()的要點(diǎn)是,如果你有一個(gè)可能是協(xié)程或Future(后者包括一個(gè)Task,因?yàn)樗荈uture的子類(lèi))的東西,并且你想能夠調(diào)用一個(gè)只在Future上定義的方法(可能唯一有用的例子是cancel())。當(dāng)它已經(jīng)是Future(或Task)時(shí),它什么也不做;當(dāng)它是協(xié)程時(shí),它將它包裝在Task中。

如果您知道您有一個(gè)協(xié)程,并且希望它被調(diào)度,那么正確的API是create_task()。唯一應(yīng)該調(diào)用ensure_future()的時(shí)候是當(dāng)你提供一個(gè)API(像大多數(shù)asyncio自己的API),它接受協(xié)程或Future,你需要對(duì)它做一些事情,需要你有一個(gè)Future。

—Guido van Rossum

總而言之,asyncio.sure_future()是一個(gè)為框架設(shè)計(jì)者準(zhǔn)備的輔助函數(shù)。這一點(diǎn)最容易通過(guò)與一種更常見(jiàn)的函數(shù)進(jìn)行類(lèi)比來(lái)解釋?zhuān)晕覀儊?lái)做這個(gè)解釋。如果你有幾年的編程經(jīng)驗(yàn),你可能已經(jīng)見(jiàn)過(guò)類(lèi)似于例3-19中的istify()函數(shù)的函數(shù)。示例 3-19中l(wèi)istify()的函數(shù)。

示例 3-19. 一個(gè)強(qiáng)制輸入列表的工具函數(shù)

這個(gè)函數(shù)試圖將參數(shù)轉(zhuǎn)換為一個(gè)列表,不管輸入的是什么。api和框架中經(jīng)常使用這類(lèi)函數(shù)將輸入強(qiáng)制轉(zhuǎn)換為已知類(lèi)型,這將簡(jiǎn)化后續(xù)代碼——在本例中,您知道參數(shù)(來(lái)自listify()的輸出)將始終是一個(gè)列表。

如果我將listify()函數(shù)重命名為ensure_list(),那么您應(yīng)該開(kāi)始看到與asyncio.ensure_future()的類(lèi)似之處:它總是試圖將參數(shù)強(qiáng)制轉(zhuǎn)換為Future(或子類(lèi))類(lèi)型。這是一個(gè)實(shí)用函數(shù),它使框架開(kāi)發(fā)人員(而不是像你我這樣的終端用戶開(kāi)發(fā)人員)的工作變得更容易。

實(shí)際上,asyncio標(biāo)準(zhǔn)庫(kù)模塊本身使用ensure_future()正是出于這個(gè)原因。當(dāng)你下次查看API時(shí),你會(huì)發(fā)現(xiàn)函數(shù)參數(shù)被描述為“可等待對(duì)象”,很可能內(nèi)部使用ensure_future()強(qiáng)制轉(zhuǎn)換參數(shù)。例如,asyncio.gather()函數(shù)就像下面的代碼一樣:

aws參數(shù)表示“可等待對(duì)象”,包括協(xié)程、task和future。在內(nèi)部,gather()使用ensure_future()進(jìn)行類(lèi)型強(qiáng)制轉(zhuǎn)換:task和future保持不變,而把協(xié)程強(qiáng)制轉(zhuǎn)為task。

這里的關(guān)鍵是,作為終端用戶應(yīng)用程序開(kāi)發(fā)人員,應(yīng)該永遠(yuǎn)不需要使用asyncio.ensure_future()。它更像是框架設(shè)計(jì)師的工具。如果你需要在事件循環(huán)上調(diào)度協(xié)程,只需直接使用asyncio.create_task()來(lái)完成。

在接下來(lái)的幾節(jié)中,我們將回到語(yǔ)言級(jí)別的特性,從異步上下文管理器開(kāi)始。

分享文章:python協(xié)程函數(shù) python協(xié)方差函數(shù)
本文地址:http://www.muchs.cn/article10/hphpgo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器、App開(kāi)發(fā)、軟件開(kāi)發(fā)、關(guān)鍵詞優(yōu)化、、域名注冊(cè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司