網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
引言
普遍意義上講,生成器是一種特殊的迭代器,它可以在執(zhí)行過(guò)程中暫停并在恢復(fù)執(zhí)行時(shí)保留它的狀態(tài)。而協(xié)程,則可以讓一個(gè)函數(shù)在執(zhí)行過(guò)程中暫停并在恢復(fù)執(zhí)行時(shí)保留它的狀態(tài),在Python3.10中,原生協(xié)程的實(shí)現(xiàn)手段,就是生成器,或者說(shuō)的更具體一些:協(xié)程就是一種特殊的生成器,而生成器,就是協(xié)程的入門(mén)心法。
協(xié)程底層實(shí)現(xiàn)
我們知道,Python3.10中可以使用async和await關(guān)鍵字來(lái)實(shí)現(xiàn)原生協(xié)程函數(shù)的定義和調(diào)度,但其實(shí),我們也可以利用生成器達(dá)到協(xié)程的效果,生成器函數(shù)和普通函數(shù)的區(qū)別在于,生成器函數(shù)使用 yield 語(yǔ)句來(lái)暫停執(zhí)行并返回結(jié)果。例如,下面是一個(gè)使用生成器函數(shù)實(shí)現(xiàn)的簡(jiǎn)單協(xié)程:
def my_coroutine():
while True:
x = yield
print(x)
# 使用生成器函數(shù)創(chuàng)建協(xié)程
coroutine = my_coroutine()
# 啟動(dòng)協(xié)程
next(coroutine)
# 在協(xié)程中傳入數(shù)據(jù)
coroutine.send(1)
coroutine.send(2)
coroutine.send(3)
程序返回:
? ?mydemo git:(master) ? /opt/homebrew/bin/python3.10 "/Users/liuyue/wodfan/work/mydemo/src/test.py" ?
1 ?
2 ?
3
在上面的代碼中,生成器函數(shù) my_coroutine 使用了一個(gè)無(wú)限循環(huán)來(lái)實(shí)現(xiàn)協(xié)程的邏輯。每當(dāng)調(diào)用 send 方法時(shí),協(xié)程就會(huì)從 yield 語(yǔ)句處恢復(fù)執(zhí)行,并將傳入的參數(shù)賦值給變量 x。
如此,就完成了協(xié)程執(zhí)行-》阻塞-》切換-》回調(diào)的工作流模式。
當(dāng)然,作為事件循環(huán)機(jī)制,協(xié)程服務(wù)啟動(dòng)可能無(wú)限期地運(yùn)行,要關(guān)閉協(xié)程服務(wù),可以使用生成器的close()方法。當(dāng)一個(gè)協(xié)程被關(guān)閉時(shí),它會(huì)生成GeneratorExit異常,該異常可以用生成器的方式進(jìn)行捕獲:
def my_coroutine():
try :
while True:
x = yield
print(x)
except GeneratorExit:
print("協(xié)程關(guān)閉")
# 使用生成器函數(shù)創(chuàng)建協(xié)程
coroutine = my_coroutine()
# 啟動(dòng)協(xié)程
next(coroutine)
# 在協(xié)程中傳入數(shù)據(jù)
coroutine.send(1)
coroutine.send(2)
coroutine.send(3)
coroutine.close()
程序返回:
? ?mydemo git:(master) ? /opt/homebrew/bin/python3.10 "/Users/liuyue/wodfan/work/mydemo/src/test.py" ?
1 ?
2 ?
3 ?
協(xié)程關(guān)閉
業(yè)務(wù)場(chǎng)景
在實(shí)際業(yè)務(wù)場(chǎng)景中,我們也可以使用生成器來(lái)模擬協(xié)程流程,主要體現(xiàn)在數(shù)據(jù)的IO流操作中,假設(shè)我們需要從本地往服務(wù)器傳輸數(shù)據(jù),首先建立鏈接對(duì)象:
class Connection:
def __init__(self, addr):
self.addr = addr
def transmit(self, data):
print(f"X: {data[0]}, Y: {data[1]} sent to {self.addr}")
隨后建立生成器函數(shù):
def send_to_server(conn):
while True:
try:
raw_data = yield
raw_data = raw_data.split(' ')
coords = (float(raw_data[0]), float(raw_data[1]))
conn.transmit(coords)
except ConnectionError:
print("鏈接丟失,進(jìn)行回調(diào)")
conn = Connection("重新連接v3u.cn")
利用生成器調(diào)用鏈接類(lèi)的transmit方法進(jìn)行數(shù)據(jù)的模擬傳輸,如果鏈接斷開(kāi),則會(huì)觸發(fā)回調(diào)重新連接,執(zhí)行邏輯:
if __name__ == '__main__':
conn = Connection("v3u.cn")
sender = send_to_server(conn)
sender.send(None)
for i in range(1, 6):
sender.send(f"{100/i} {200/i}")
# 模擬鏈接斷開(kāi)
conn.addr = None
sender.throw(ConnectionError)
for i in range(1, 6):
sender.send(f"{100/i} {200/i}")
程序返回:
X: 100.0, Y: 200.0 sent to v3u.cn
X: 50.0, Y: 100.0 sent to v3u.cn
X: 33.333333333333336, Y: 66.66666666666667 sent to v3u.cn
X: 25.0, Y: 50.0 sent to v3u.cn
X: 20.0, Y: 40.0 sent to v3u.cn
鏈接丟失,進(jìn)行回調(diào)
X: 100.0, Y: 200.0 sent to 重新連接v3u.cn
X: 50.0, Y: 100.0 sent to 重新連接v3u.cn
X: 33.333333333333336, Y: 66.66666666666667 sent to 重新連接v3u.cn
X: 25.0, Y: 50.0 sent to 重新連接v3u.cn
X: 20.0, Y: 40.0 sent to 重新連接v3u.cn
如此,我們就可以利用生成器的“狀態(tài)保留”機(jī)制來(lái)控制網(wǎng)絡(luò)鏈接突然斷開(kāi)的回調(diào)補(bǔ)救措施了。
所以說(shuō),協(xié)程就是一種特殊的生成器:
async def test():
pass
print(type(test()))
您猜怎么著?
<class 'coroutine'>
結(jié)語(yǔ)
誠(chéng)然,生成器和協(xié)程也并非完全是一個(gè)概念,與生成器不同的是,協(xié)程可以被另一個(gè)函數(shù)(稱(chēng)為調(diào)用方)恢復(fù)執(zhí)行,而不是只能由生成器本身恢復(fù)執(zhí)行。這使得協(xié)程可以用來(lái)實(shí)現(xiàn)更復(fù)雜的控制流,因?yàn)樗鼈兛梢栽趫?zhí)行時(shí)暫停并在任意時(shí)刻恢復(fù)執(zhí)行。
原文鏈接:https://juejin.cn/post/7181766056719941690
相關(guān)推薦
- 2022-04-19 Django項(xiàng)目中動(dòng)態(tài)設(shè)置靜態(tài)文件路徑的全過(guò)程_python
- 2022-04-14 Django執(zhí)行指定腳本的幾種方法_python
- 2023-05-23 numpy中的掩碼數(shù)組的使用_python
- 2022-12-13 深入了解Rust的生命周期_Rust語(yǔ)言
- 2023-09-12 Spring Boot自動(dòng)配置介紹
- 2022-07-06 C語(yǔ)言中#pragma?once的作用_C 語(yǔ)言
- 2022-03-19 詳解C語(yǔ)言結(jié)構(gòu)體的定義和使用_C 語(yǔ)言
- 2022-07-01 Android?TextView跑馬燈實(shí)現(xiàn)原理及方法實(shí)例_Android
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支