Python:迭代器
本文参考博客https://www.cnblogs.com/huchong/p/7274073.html
1. 在讲迭代器之前,我们先来讲一下迭代,什么是迭代呢?
可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代。比如我们平时用的的for循环····
2. 可以满足迭代要求的就叫做可迭代协议。可迭代协议的定义,就是内部实现了__iter__方法。
如下:像列表、元组、字典、集合等都是可迭代对象,都有__iter__方法。
from collections import Iterable # isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。 #验证是否满足迭代:全部输出TRUE,是可迭代的 l = [1, 2, 3, 4] t = (1, 2, 3, 4) d = {"name":"tom", "age": 44} s = {1, 2, 3, 4} print(isinstance(l, Iterable)) print(isinstance(t, Iterable)) print(isinstance(d, Iterable)) print(isinstance(s, Iterable)) print(dir(d)) #包含__iter__方法
3.迭代器协议
''' dir(): 不带参数时,返回当前范围内的变量、方法和定义的类型列表; 带参数时,返回参数的属性、方法列表。 dir([1,2].__iter__())是列表迭代器中实现的所有方法, dir([1,2])是列表中实现的所有方法, 以上都是以列表的形式返回给我们的,通过分别把他们转换成集合取差集,我们得到了 列表迭代器比列表多什么方法 ''' print(dir([1,2].__iter__())) print(dir([1,2])) print(set(dir([1,2].__iter__()))-set(dir([1,2])))
# 最终结果:
# {'__length_hint__', '__next__', '__setstate__'}
可以看到列表迭代器多了3种方法,那这三种方法有什么作用呢?
'''迭代器 3个方法的使用''' list=[1,2,3,4,5,6] iter_l = list.__iter__() #获取迭代器中元素的长度 print(iter_l.__length_hint__()) #根据索引值指定从哪里开始迭代,此处指定为从3开始,默认从1开始的。 print('修改迭代的位置,从序号3开始',iter_l.__setstate__(3)) #一个一个的取值 print('从序号3开始,第一次取值为:',iter_l.__next__()) print('从序号3开始,第二次取值为:',iter_l.__next__())
#输出结果:
6
修改迭代的位置,从序号3开始 None
从序号3开始,第一次取值为: 4
从序号3开始,第二次取值为: 5
"""
在for循环就是调用了__next__方法。
尝试不使用for循环,通过迭代器__next__对列表进行迭代;如果已经迭代完,再迭代会报错StopIteration。
可以使用while语句进行容错处理,完成for循环所做的事情,我们是从哪里取值呢,是从l_iter中取数据,l_iter就是一个迭代器。
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
"""
l = [1,2,3,4] l_iter = l.__iter__() #-----------直接调用__next__执行------------------------ item = l_iter.__next__() print(item) # 输出1 item = l_iter.__next__() print(item) # 输出2 item = l_iter.__next__() print(item) # 输出 3 item = l_iter.__next__() print(item) # 输出4 item = l_iter.__next__() print(item) # 超出边界值,报错:StopIteration #-----------使用While方法执行------------------------ while True: try: item = l_iter.__next__() print(item) # 依次输出 1 2 3 4 except StopIteration: # 遇到报错退出循环 break
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
我们来看看range()函数是个啥?首先它肯定是可迭代的对象,是不是迭代器呢,执行下面的代码后发现range()函数不是个迭代器。
print('__next__' in dir(range(12))) #查看在range()方法执行之后内部是否有__next__ FALSE print('__iter__' in dir(range(12))) #查看在range()方法执行之后内部是否有__iter__ TRUE from collections import Iterator print(isinstance(range(1000),Iterator)) #验证range执行之后得到的结果不是一个迭代器 FALSE print(isinstance(range(1000).__iter__(),Iterator)) # 加了__iter__()方法后,结果返回true,是一个迭代器
由此揭露了For循环的本质:循环所有的对象,全部都是使用的迭代器协议。
字符串,列表,元组,字典,集合,文件对象这些统统都不是迭代器,只不过在for循环时,调用了他们内部的__iter__方法,把他们变成了迭代器。如:
print(isinstance(range(1000).__iter__(),Iterator)) # 加了__iter__()方法后,结果返回true,是一个迭代器
然后for循环调用迭代器的__next__方法去取值,而且for循环会捕捉StopIteration异常,以终止迭代。
总结:
序列类型:字符串,列表,元组都有下标,可以用上述的方式访问。
但是,非序列类型:像字典,集合,文件呢?
for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,
即在遍历之前, 先调用对象的__iter__方法将其转换成一个迭代器,
然后使用迭代器协议去实现循环访问,这样所有的对象就都可以通过for循环来遍历了