python代码生成器原理(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>结语
诚然 ,生成器和协程也并非完全是一个概念 ,与生成器不同的是 ,协程可以被另一个函数(称为调用方)恢复执行 ,而不是只能由生成器本身恢复执行 。这使得协程可以用来实现更复杂的控制流 ,因为它们可以在执行时暂停并在任意时刻恢复执行。
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!