#迭代器
#什么是迭代器协议?
是指对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,否则引起一个stop Iteration异常,以终止迭代。并且不可逆。
对象1 -->提供一个next方法 --> 调用对象的next方法(对象.next) -->对象2 ...... --> 直到无法生成下一项,引起stopIteration异常,报错
#可迭代对象
遵循迭代器协议的对象
#协议
协议是一种约定,可迭代对象实现了迭代器协议,python的一些内部工具(如for、sum、min、max函数)使用迭代器协议访问对象
#修正一下之前的一些误区
前面的章节我们都认为,(字符串、列表、元组、字典、集合、文件对象)都为可迭代对象,其实不然
他们本身均不遵循迭代器协议,不信你试试 \'list\'.__next__()(报错) 他们并没有 next方法,故他们不是可迭代对象
那么问题来了,为什么我们能用for循环他们呢? for循环不是使用迭代器协议访问对象吗?
其实在使用for循环时,系统内部自动的进行了一个操作,即 对象1 = 对象.__iter__() ,这样对象1就为一个迭代器(可迭代对象)来代替原来的对象用于执行for循环
然后不断执行对象.__next__(),直到发现stopIteration异常,终止。
虽然如列表可用while循环通过索引的方式遍历所有元素,但无序的字典、集合则无法索引。于是for其实给各种类型提供了一个统一的遍历手段,
text = \'hello\' for i in text : text_new = text.__iter__() --> 相当于 print(i) 里的一次迭代 print(text_new.__next__())
#用while循环模拟for循环
a = \'abcdf23\' a1 = a.__iter__()
for i in a :
print(i)
#相当于 while True : try: #捕捉异常 print(a1.__next__()) except StopIteration : print(\'迭代完毕,循环终止\') break
#iter函数
将符合迭代器协议的对象转换为迭代器,其第二个参数控制迭代到什么时候为止
1 a = [\'a\', \'b\', \'c\', \'d\'] 2 def test(): 3 return a.pop() 4 #第二个参数\'b\'代表一直迭代直到值为\'b\'的时候停止 5 b = iter(test, \'b\') 6 for i in b: 7 print(i)
小补充:
a.__next__ == next(a)
迭代器就是可迭代对象,值为地址的形式
#生成器
#生成器可以看做是一种数据类型,本身就遵循迭代器协议
#生成器是一种特殊的迭代器
#如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)
#生成器只能遍历一次
def iteration() : for i in range(1,6) : yield \'迭代第%s次\' %(i) a = iteration() for item in a : print(item) print(a.__next__()) -->StopIteration
#生成器函数
#yield语句(可被send赋值) , 代替return (返回一个生成器,相当于自动帮你完成了__iter__) ,并且可以返回多次
#可以保留函数的状态 执行函数 --> 碰上第一个yield 返回一个生成器 ,函数暂停--> 返回的生成器执行了一个 next方法 --> 函数从暂停位置(第一个yield处)继续执行,碰上第二个yield 返回一个生成器
#好处,函数每完成一部分的数据都可以马上进行下一步处理,而不是全部完成后再一起处理
def a() : for i in range(1,10) : print(\'开始迭代第%s次\' %i) yield \'第%s次迭代完毕\' %i print(\'准备下一次迭代\') b =a() print(b.__next__()) print(b.__next__()) print(b.__next__())
#send语句,除了有__next__()的进行下一次迭代的功能,还能给yield传值
def a() : for i in range(1,10) : print(\'开始迭代第%s次---->函数执行的结果\' %i) res=yield \'第%s次迭代完毕(这是返回值,函数外部print的结果)\' %i print(res) b =a() print(b.__next__()) #第一次迭代只能传 None 因为它是传给上一次迭代的yield print(b.__next__()) #yield不用send传值默认为None print(b.send(\'准备下一次迭代\'))
#三元表达式
name = \'wwj\' res = \'beautiful girl\' if name == \'wwj\' else \'get out\' print(res)
grade1 = [\'成绩 = %s\' %(i) for i in range(10) if i > 5] print(grade1)
#列表解析(简洁的生成列表)
grade = [] for i in range(10) : grade.append(\'成绩 = %s\' %(i)) print(grade) #以下代码用列表解析来完成上文相同的功能 grade1 = [\'成绩 = %s\' %(i) for i in range(10)] print(grade1) #但是以上方法仍有不足,以上等于是将所有列表元素都加载到内存中,占用内存空间大 #以下用生成器表达式(把列表解析的[]换成()就行) grade1 = ( \'成绩 = %s\' %(i) for i in range(10) if i > 5 ) print(grade1)
#生成器表达式
#把列表转换为生成器,节省内存空间
l = [1,2,3,4,\'sdfadasf\',[\'asdfds\']] generator = (i for i in l ) print(generator) while True : try : print(generator.__next__()) except StopIteration : print(\'迭代完毕\') break
#小补充
l = [1,2,3,4] sum(l)
实际等于
list = [1,2,3,4] sum(i for i in list) #所以sum()函数默认帮我们做了一个转换为生成器的操作
生成器也能被for循环
def iteration() : for i in range(1,100) : yield \'迭代第%s次\' %(i) for item in iteration() : print(item)
#一个生成器的小例子,计算人口百分比(比直接获取整个人口普查文本然后放入列表中要节省内存空间的多)
#\'人口普查\'文本
{\'地区\':\'北京\' , \'人口\' : 10232142}
{\'地区\':\'新疆\' , \'人口\' : 102322}
{\'地区\':\'青岛\' , \'人口\' : 3223322}
#获取地区及其人口所占百分比
def population() : with open(\'人口普查\',\'r\',encoding=\'utf-8\') as f : # num = 0 # for i in f : # num += eval(i)[\'人口\'] num = sum(eval(i)[\'人口\'] for i in f) #比上几行的代码简洁优美 f.seek(0) #此时生成器已经遍历完,需要seek返回文章开头重新遍历 for i in f : a = eval(i)[\'地区\'] b = eval(i)[\'人口\'] yield a,"人口百分比 %%%f"%(b/num*100) a = population() print(a.__next__()) print(a.__next__()) print(a.__next__())
#产生和处理同时完成(同时执行两个程序),单线程里的并发
def consumer(num): #获取产生的值,并进行处理 print(\'%s 开始照镜子\' %num) while True: res=yield print(\'偷偷对%s使用变美喷雾 颜值+%d\' %(num,res)) if res==5 : break def producer(name): #产生值 b = consumer(\'%s\' % name) b.__next__() for i in range(1,6): try: b.send(i) except StopIteration : print(\'wwj说:哇,我怎么这么美\') break producer(\'wwj\')