python 迭代器|生成器
迭代器 |
- 迭代器的概述
迭代是指重复从对象中获取数据,直至结束。而所谓的迭代协议,概括起来就是用__iter__方法返回一个实现了__next__方法的迭代器对象。
迭代器使用了分离设计,是访问集合元素的一种方式。首先,对目标对象而言,迭代器是一种与自身逻辑无关的用户接口,组合显然比内联合适;其次,迭代分多次完成,需要保存进度。
而且还可能有重复迭代,以及同时进行多个迭代的情形。如何存储并管理这些状态?不如每次按需新建实例,单向存储进度。用完即毁,无需考虑太多。
- 迭代器的特点
- 访问者不需要关心其内部结构,仅需通过__next__()方法不断去取下一个内容。
- 迭代器不能随机访问,只能依次从头访问。
- 迭代器进行元素访问时,不能回退。
- 迭代器是单向存储进度,只进不退,空间用完即毁,节省内存。
- 迭代器的操作
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的特点
- yield 是一个类似 return 的关键字,迭代一次即调用一次__next__,当程序遇到yield时就停止便返回函数初始位置,下次迭代时,直接从yield后面的代码(下一行)开始执行。
- yield可以随时切入切出循环,便是因为特点1.
- 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完全一致。不过发送之前,须确保生成器已经启动。因为只有如此,才会进入函数执行流程,才会对外提供数据和交互。