0

Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程as...

 1 year ago
source link: https://v3u.cn/a_id_266
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现

    普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态。而协程,则可以让一个函数在执行过程中暂停并在恢复执行时保留它的状态,在Python3.10中,原生协程的实现手段,就是生成器,或者说的更具体一些:协程就是一种特殊的生成器,而生成器,就是协程的入门心法。

    协程底层实现

    我们知道,Python3.10中可以使用async和await关键字来实现原生协程函数的定义和调度,但其实,我们也可以利用生成器达到协程的效果,生成器函数和普通函数的区别在于,生成器函数使用 yield 语句来暂停执行并返回结果。例如,下面是一个使用生成器函数实现的简单协程:

def my_coroutine():
while True:
x = yield
print(x)

# 使用生成器函数创建协程
coroutine = my_coroutine()

# 启动协程
next(coroutine)

# 在协程中传入数据
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

    在上面的代码中,生成器函数 my_coroutine 使用了一个无限循环来实现协程的逻辑。每当调用 send 方法时,协程就会从 yield 语句处恢复执行,并将传入的参数赋值给变量 x。

    如此,就完成了协程执行-》阻塞-》切换-》回调的工作流模式。

    当然,作为事件循环机制,协程服务启动可能无限期地运行,要关闭协程服务,可以使用生成器的close()方法。当一个协程被关闭时,它会生成GeneratorExit异常,该异常可以用生成器的方式进行捕获:

def my_coroutine():
try :
while True:
x = yield
print(x)
except GeneratorExit:
print("协程关闭")

# 使用生成器函数创建协程
coroutine = my_coroutine()

# 启动协程
next(coroutine)

# 在协程中传入数据
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
协程关闭

    在实际业务场景中,我们也可以使用生成器来模拟协程流程,主要体现在数据的IO流操作中,假设我们需要从本地往服务器传输数据,首先建立链接对象:

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}")

    随后建立生成器函数:

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("链接丢失,进行回调")
conn = Connection("重新连接v3u.cn")

    利用生成器调用链接类的transmit方法进行数据的模拟传输,如果链接断开,则会触发回调重新连接,执行逻辑:

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}")

# 模拟链接断开
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
链接丢失,进行回调
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

    如此,我们就可以利用生成器的“状态保留”机制来控制网络链接突然断开的回调补救措施了。

    所以说,协程就是一种特殊的生成器:

async def test():
pass

print(type(test()))

    您猜怎么着?

<class 'coroutine'>

    诚然,生成器和协程也并非完全是一个概念,与生成器不同的是,协程可以被另一个函数(称为调用方)恢复执行,而不是只能由生成器本身恢复执行。这使得协程可以用来实现更复杂的控制流,因为它们可以在执行时暂停并在任意时刻恢复执行。    


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK