Python之生成器

迭代器#

使用方式#

在Python中只要包含 __iter__()__next__()方法即属于迭代器,前者返回迭代器对象,后者返回迭代器对象所对应的值,直到引发 StopIteration异常结束

最简单的方法,使用内置的方法 iter(),其返回常用类型的迭代器包装对象

Copy
class Data: def __init__(self): self.datas = [] def add(self, x): self.datas.append(x) def data(self): return iter(self.datas) d = Data() d.add(1) d.add(2) d.add(3) print(type(d.data())) for x in d.data(): print(x)

image-20210419214456947

自定义迭代器#

返回迭代器对象代替 self.datas列表,可避免对象状态被外部所修改,如果使用 tuple需要复制整个列表,浪费更多的内存

iter()很方便,但无法让迭代中途停止,这需要自己手动实现迭代器对象,将迭代器从数据对象中分离出去,防止迭代器需要维持状态,同时可能多个迭代器在同时操控数据,这些不该成为数据对象的负担,提升复杂度

Copy
from collections import Iterable # 自定义迭代对象 class Data: def __init__(self): self.items = [] def append_items(self, data): self.items.append(data) # 定义iter方法 返回对应的迭代器 def __iter__(self): return DataIterator(self.items) # 自定义迭代器对象 class DataIterator: # 在类里面定义__iter__和__next__方法创建的对象就是迭代器对象 def __init__(self, items): self.items = items self.current_index = 0 # 迭代器取值 def __next__(self): print("当前容器对象:%s" % self.items) if self.current_index >= len(self.items): raise StopIteration value = self.items[self.current_index] self.current_index += 1 return value if __name__ == '__main__': data = Data() data.append_items(11) data.append_items(12) data.append_items(13) print(data) res = isinstance(data, Iterable) print('是否为可迭代对象:%s' % res) for i in data: print("迭代器取值:%s" % i)

image-20210419225628936
在上述代码中Data仅仅为数据容器只需要使用 __iter__返回迭代器对象即可,而由DataIterator提供 __next__方法进行数据取值,除了使用for循环进行取值,迭代器也可以使用 __next__取值

Copy
it = iter(data) for i in range(4): print(it.__next__())

image-20210419230528217

生成器#

基于索引实现的迭代器有些丑陋,更合理的的做法使用 yield返回实现了迭代器协议的 Generator对象

Copy
class Data: def __init__(self): self.items = [] def add(self, data): self.items.append(data) def __iter__(self): for i in self.items: yield i d = Data() d.add(11) d.add(12) d.add(13) print(d) # 编译器会将包含 `yield`方法的函数进行重新打包,时期返回 ` Generator`对象 print(d.__iter__()) for x in d: print(x)

image-20210419234604787

协程#

yield实现生成 Generator涉及到协程的工作原理

Copy
def crountinnne(): print('croutine start') result = None while True: res = yield result print(res) result = res.split(",") c = crountinnne() c.send(None) c.send('a,b')

image-20210420230843303

  • 创建协程对象,必须使用 send(None)或者 Next对象启动协程
  • 协程在执行到 res = yield result的时候将程序挂起,等待消息
  • 调用方发送 'a,b'消息,协程恢复执行,将接收消息保存给 res,继续执行接下流程
  • 程序再次循环到 yield将上次处理结果返回,再次将程序挂起,等待消息
  • 直到程序结束或者引发异常
Copy
# 关闭协程 c.close() # 无法发送消息 c.send('c,d')

image-20210420232857978
close可以引发协程 GeneratorExit使其正常退出,而 throw可以引发任何类型的异常

模式#

生产消费模型#

利用 yield协程特性,进行生产者消费者模型

Copy
def consumer(): while True: d = yield if not d: break print("消费者开启:%s" % d) # 创建消费者 c = consumer() # 启动消费者 c.send(None) c.send(1) c.send(2) c.send(3) # 生产结束 通知消费者结束 c.send(None)

image-20210422202157717

异步回调#

回调函数是异步操作常用的手段,使用 yield完全可以使用 blocking style(非阻塞)进行异步操作

下列代码中首先 framework是主函数,调用 logic函数,当 logic函数被调用接受到某个结果或者信号的时候,会使用 callback函数进行结果返回,但是此种方法被拆分到两个函数里,业务逻辑不清晰

Copy
def framework(logic,callback): s = logic() print("logic函数开始运行:%s" % logic) print("framework函数开始运行") # 调用回调函数返回结果 callback("async:%s" % s) def logic(): s = 'logic' return s def callback(s): print(s) framework(logic,callback)

image-20210423081454105
使用 yield进行 blockins style回调

Copy
def framework(logic): try: # 获取生成器 it = logic() print(it) # 获取生成器的值 s = next(it) print("logic函数运行:%s" % s) print("framework处理任务:") # 对回调函数发送信号 it.send("async:%s" % s) except StopIteration: # 遇到异常 打破循环 pass def logic(): s = 'mylogic' res = yield s print(res) framework(logic)

image-20210423090940746

posted @   SR丶  阅读(91)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示
CONTENTS