Python之生成器
迭代器
使用方式
在Python中只要包含 __iter__()
与 __next__()
方法即属于迭代器,前者返回迭代器对象,后者返回迭代器对象所对应的值,直到引发 StopIteration
异常结束
最简单的方法,使用内置的方法 iter()
,其返回常用类型的迭代器包装对象
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)
自定义迭代器
返回迭代器对象代替 self.datas
列表,可避免对象状态被外部所修改,如果使用 tuple
需要复制整个列表,浪费更多的内存
iter()
很方便,但无法让迭代中途停止,这需要自己手动实现迭代器对象,将迭代器从数据对象中分离出去,防止迭代器需要维持状态,同时可能多个迭代器在同时操控数据,这些不该成为数据对象的负担,提升复杂度
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)
在上述代码中Data仅仅为数据容器只需要使用 __iter__
返回迭代器对象即可,而由DataIterator提供 __next__
方法进行数据取值,除了使用for循环进行取值,迭代器也可以使用 __next__
取值
it = iter(data)
for i in range(4):
print(it.__next__())
生成器
基于索引实现的迭代器有些丑陋,更合理的的做法使用 yield
返回实现了迭代器协议的 Generator
对象
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)
协程
yield
实现生成 Generator
涉及到协程的工作原理
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')
- 创建协程对象,必须使用
send(None)
或者Next
对象启动协程 - 协程在执行到
res = yield result
的时候将程序挂起,等待消息 - 调用方发送
'a,b'
消息,协程恢复执行,将接收消息保存给res
,继续执行接下流程 - 程序再次循环到
yield
将上次处理结果返回,再次将程序挂起,等待消息 - 直到程序结束或者引发异常
# 关闭协程
c.close()
# 无法发送消息
c.send('c,d')
close
可以引发协程 GeneratorExit
使其正常退出,而 throw
可以引发任何类型的异常
模式
生产消费模型
利用 yield
协程特性,进行生产者消费者模型
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)
异步回调
回调函数是异步操作常用的手段,使用 yield
完全可以使用 blocking style
(非阻塞)进行异步操作
下列代码中首先 framework
是主函数,调用 logic
函数,当 logic
函数被调用接受到某个结果或者信号的时候,会使用 callback
函数进行结果返回,但是此种方法被拆分到两个函数里,业务逻辑不清晰
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)
使用 yield
进行 blockins style
回调
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)