python 迭代器|生成器

迭代器
  • 迭代器的概述

  迭代是指重复从对象中获取数据,直至结束。而所谓的迭代协议,概括起来就是用__iter__方法返回一个实现了__next__方法的迭代器对象。

  迭代器使用了分离设计,是访问集合元素的一种方式。首先,对目标对象而言,迭代器是一种与自身逻辑无关的用户接口,组合显然比内联合适;其次,迭代分多次完成,需要保存进度。

而且还可能有重复迭代,以及同时进行多个迭代的情形。如何存储并管理这些状态?不如每次按需新建实例,单向存储进度。用完即毁,无需考虑太多。

  • 迭代器的特点
  1. 访问者不需要关心其内部结构,仅需通过__next__()方法不断去取下一个内容。
  2. 迭代器不能随机访问,只能依次从头访问。
  3. 迭代器进行元素访问时,不能回退。
  4. 迭代器是单向存储进度,只进不退,空间用完即毁,节省内存。
  • 迭代器的操作

  iter(data)

  实例:

#列表
n1 = iter(["you","are","a","pig",2333])
print(n1.__next__())
print(n1.__next__())
print(n1.__next__())
print(n1.__next__())
print(n1.__next__())

输出结果:  

  you
  are
  a
  pig
  2333

由结果可知,每次调用__next__方法太过繁琐,故可对其加以修改,使用for循环使其自动迭代。

n1 = iter(["you","are","a","pig",2333])
for i in n1:
    print(i)

  尽管列表、字典等容器类型实现了迭代协议。但本质上,两者不属于同一层面。迭代器不仅是一种数据读取方法,而更多的是一种设计模式。

  迭代器的重点是逻辑控制。调用方法发出请求,随后决策由迭代器决定。数据收敛,抽象和实现分离。

生成器(generator)
  • 生成器的概述  

  生成器是迭代器的进化版本,用函数和表达式替代接口方法。其不仅简化了编码过程,还提供了更多控制能力用于复杂设计。生成器函数的特殊之处在于,其内部以yield返回迭代数据。这与普通函数不同,无论内部逻辑如何,其函数调用总是返回生成器对象。随后,以普通迭代器方式继续操作。

  • yield的特点
  1. yield 是一个类似 return 的关键字,迭代一次即调用一次__next__,当程序遇到yield时就停止便返回函数初始位置,下次迭代时,直接从yield后面的代码(下一行)开始执行。
  2. yield可以随时切入切出循环,便是因为特点1.
  3. yield可以随时中断一个函数。以yield为切换分界线,往复交替,直至函数结束。
def store_sort(count):
    while count>0:
        count -= 1
        yield 1
        print("胖成猪了,还吃!!!")

if __name__ == "__main__":
    you = store_sort(10)
    print(you.__next__())
    print(you.__next__())
    print(you.__next__())

输出结果:

  1
  胖成猪了,还吃!!!
  1
  胖成猪了,还吃!!!
  1

  由结果可知,每条yield语句对应一次__next__调用。可分裂多条__next__语句,或出现在循环语句中,只要结束函数流程,就相当于抛出迭代终止异常。

  由上述列子store_sort(count)函数可知,只要有yield在整个函数便是一个生成器,而它返回的是一个迭代器。

  使用yield还可以实现单线程中的异步并发效果,例如生产者--消费者模型

import time

def consumer(name):
    print("%s 准备吃包子啦!"%name)
    while True:
        baozi = yield
        '''
        yield既可以返回一个值,也可以接受一个值。
        返回一个值:如 yield 1
        接收一个值:如 baozi = yield
        '''
        print("包子%s来啦,被%s吃了!"%(baozi,name))


def producer(name):
    c1 = consumer("小猪")
    c2 = consumer("佩奇")
    c1.__next__()
    c2.__next__()
    print("傻妞开始做包子啦!")
    for i in range(1,3):
        time.sleep(1)
        print("傻妞做了两个包子")
        c1.send(i)
        c2.send(i)
if __name__ == "__main__":
    producer("傻妞")

输出结果:

  小猪 准备吃包子啦!
  佩奇 准备吃包子啦!
  傻妞开始做包子啦!
  傻妞做了两个包子
  包子1来啦,被小猪吃了!
  包子1来啦,被佩奇吃了!
  傻妞做了两个包子
  包子2来啦,被小猪吃了!
  包子2来啦,被佩奇吃了!

  • 生成器方法

  生成器的另一进化特征,就是提供双向通信能力。

  生成器不再是简单的数据提供方(yield 1),其还可以作为接收方(baozi = yield)存在。如上述两个例子所示。生成器甚至能在外部停止迭代,或发送信号实现重置等自定义行为。

  方法send除可以向yield传送数据外,其他与next完全一致。不过发送之前,须确保生成器已经启动。因为只有如此,才会进入函数执行流程,才会对外提供数据和交互。

posted @ 2019-02-26 17:35  栗子姑娘  阅读(102)  评论(0编辑  收藏  举报