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)

image-20210419214456947

自定义迭代器

返回迭代器对象代替 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)

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

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

image-20210419230528217

生成器

基于索引实现的迭代器有些丑陋,更合理的的做法使用 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)

image-20210419234604787

协程

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')

image-20210420230843303

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

# 无法发送消息
c.send('c,d')

image-20210420232857978
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)

image-20210422202157717

异步回调

回调函数是异步操作常用的手段,使用 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)

image-20210423081454105
使用 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)

image-20210423090940746

posted @ 2021-04-23 09:23  SR丶  阅读(88)  评论(0编辑  收藏  举报