什么是迭代器?
首先我们得明白迭代是什么,迭代是一个重复的过程,下一次结果的产生是基于上一次的结果。
python中为什么会有迭代器,我们知道python中一些有索引的序列类型,如list,tuple,str,我们可以使用索引的方式迭代取出其包含的元素。
但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,也就是它应该具有遍历复杂数据结构的能力,这就是迭代器。
什么是可迭代对象?
在python中可迭代对象指的是内置有__iter__方法的对象。
#可迭代对象 l=[0,8] dic={'das':88} l.__iter__() dic.__iter__()
什么是迭代器对象?
在python中迭代器对象指的是即内置有__iter__又内置有__next__方法的对象
#迭代器对象 with open('ad.txt','r') as f: f.__iter__() f.__next__()
可迭代对象和迭代器对象都有__iter__方法,那么有什么区别呢?
可迭代对象.__iter__()得到的是一个迭代器对象
迭代器对象.__iter__()得到的仍然是迭代器对象本身
也就是说迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
l=[0,8] dic={'das':88} obj=l.__iter__() obj1=dic.__iter__() obj.__next__() obj.__iter__() obj1.__next__() obj1.__iter__() #迭代器对象 with open('ad.txt','r') as f: f1=f.__iter__() print(f1==f,f1 is f)#这里是true f.__next__()
迭代器对象.__next__()方法是做什么的呢
这里的__next__()方法就是用来取值的
l=[0,8]#l是可迭代对象 obj_l=l.__iter__()#obj_l是迭代器对象 print(obj_l.__next__())#输出0 print(obj_l.__next__())#输出8 print(obj_l.__next__())#报错StopIteration
这里我们没有按索引取值,但是去拿到了列表l里的值。那我们再来看看其他没有索引的
dic={'x':221,'y':3213,'z':132} iter_dic=dic.__iter__() #先得到迭代器对象,不管是迭代器对象还是可迭代对象 print(iter_dic.__next__()) print(next(iter_dic)) #等同于iter_dic.__next__() print(iter_dic.__next__()) #当然这里3个输出会是无序的 ---->x y z print(iter_dic.__next__()) #再继续就,抛出异常StopIteration,或者说结束了
当然也可以用循环的方式取值
dic={'x':221,'y':3213,'z':132} iter_dic=dic.__iter__()#先得到迭代器对象,不管是迭代器对象还是可迭代对象 while True: try: print(next(iter_dic)) # 等同于iter_dic.__next__() 取出来是dic的键 except StopIteration: break
上面的循环取值就跟python中的for循环一样的
这里我们可以了解一下for循环的机制:
dic={'x':221,'y':3213,'z':132} for i in dic: print(i)
1:先执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
2: 执行iter_dic.__next__()或是next(iter_dic),将得到的值赋值给i,然后执行循环体代码(类似上面的while循环)
3: 重复过程2,直到捕捉到异常StopIteration,结束循环
重要的是for循环in后面的对象一定得是可迭代对象!!!
迭代器的优缺点:
优点: 提供不依赖于索引的取值方式 惰性计算,节省内存,这里可以去打印迭代器看看, 只有next的时候才计算值
缺点: 无法获取长度(只有在next完毕才知道到底有几个值),打印迭代器看到的是一个地址 一次性的,只能往后走,不能往前退(只能一条道走到黑,回不到从前了)
生成器是什么呢?
生成器是一种迭代器(可以自定义的迭代器),特性:迭代一次生成一个值。
我们得到一个生成器呢,这里有一种方式:
定义一个函数,其内部包含有yield关键字,那么调用一下得到的结果就是生成器,并且不会执行函数内部代码
def get(): print(222) yield 2 print('ada') yield 3 it=get() print(it)#<generator object get at 0x050C3FC0> print(it.__next__())#222 2 print(it.__next__())#ada 3 print(it.__next__())#报错StopIteration
这里我们发现当我们调用生成器it的next方法时,它遇到yield才停下来,然后把yiled后的值返回来
继续next的话会跳到下一个yield 然后把yiled后的值返回来,若next下去的话没有yield 则会报错。
也就是说yield后的值在调用一次next时会被返回一次。
这样的话我们就有操作空间了,对yield后的值进行自定义我们想要的,也就是可以自定义的迭代器。
仿写一个range的例子:
def my_range(start,end,step=1): while start < end: yield start start+=step for i in my_range(0,22,3): print(i)
当然yield还有其他用处,这里的yield只是提供一种自定义迭代器的方式。