python迭代器和生成器

什么是迭代器?

首先我们得明白迭代是什么,迭代是一个重复的过程,下一次结果的产生是基于上一次的结果。

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只是提供一种自定义迭代器的方式。

 

posted @ 2018-07-11 10:29  孤月灵声  阅读(144)  评论(0编辑  收藏  举报