迭代器指的是迭代取值的工具,迭代指的是一个重复的过程,每一次重复都是基于上一次的结果而继续,单纯的重复并不是迭代
count = 0
while count < 5:
print(count)
count += 1
迭代器涉及到把多个值循环取出来的工作,涉及类型包括:列表、字符串、元组、字典、集合、文件
# 现在我们暂时忘记for循环,将下列列表取出
l = ['egon','liu','alex']
list_len = len(l)
while list_len > 0:
list_len = list_len - 1
print(l[list_len])
# 此取值方式是根据索引自减进行的,不适用于没有索引类型的取值(例如字典)
为了解决基于索引取值的局限性,python必须提供一种不依赖于索引的取值方式,就是迭代器
可迭代对象
在了解迭代器之前,我们首先需要了解一下可迭代对象
在python语法中,但凡内置有__iter__
方法的都称之为可迭代的对象,换个说法就是可以使用__iter__
方法将可迭代对象转换为迭代器对象
可迭代对象:字符串、列表、元组、字典、集合、文件对象
s1 = ''
s1.__iter__()
l = []
l.__iter__()
t = {'a',}
t.__iter__()
d = {'a':1}
d.__iter__()
set = {1,2,3}
set.__iter__()
with open('user',mode='rt',encoding='utf-8') as f:
f.__iter__()
迭代器
调用可迭代对象下的__iter__
方法会将其转换成迭代器对象
d = {'a':1,'b':2,'c':3}
res = d.__iter__()
print(res) # <dict_keyiterator object at 0x0000015A3E5F0270>
转换为迭代器对象之后,可以使用__next__
进行取值
d = {'a':1,'b':2,'c':3}
d_iter = d.__iter__()
print(d_iter) # <dict_keyiterator object at 0x0000015A3E5F0270>
# 三次取值会依次取元素,取值次数超过元素数,则报错
print(d_iter.__next__()) # a
print(d_iter.__next__()) # b
print(d_iter.__next__()) # c
print(d_iter.__next__()) # 抛出异常StopIteration
修改循环取值代码
d = {'a':1,'b':2,'c':3}
d_iter = d.__iter__()
while True:
try:
print(d_iter.__next__())
except StopIteration: # 捕捉到错误语法StopIteration执行下方代码
break
try...except...
捕捉错误语法,例如上文代码就是捕捉错误语法StopIteration
for循环的工作原理
其实for循环的工作原理和之前的while循环取值代码是一样的,所以for循环又被称为迭代器循环
dic = {'a':1,'b':2,'c':3}
for k in dic:
print(k)
# 1. dic.__iter__()得到迭代器对象
# 2. 迭代器对象.__next__()拿到一个返回值,然后将返回值复制给k
# 3. 循环往复步骤2,直到抛出异常StopIteration异常,for循环捕捉异常,结束循环
但是我们这里需要注意这么一个问题
dic = {'a':1,'b':2,'c':3}
# 转换为迭代器对象
dic_iter = dic.__iter__()
# 发现迭代器对象也可以进行__iter__转换
dic_iter.__iter__()
# 发现dic_iter和dic_iter.__iter__()的内存地址都没有变化,是否调用一个样
# 3144960901824 3144960901824
print(id(dic_iter),id(dic_iter.__iter__()))
问题来了,迭代器对象的__iter__
有什么用?
本文可迭代对象部分提到过,可迭代对象:字符串、列表、元组、字典、集合和文件对象
with open('user',mode='rt',encoding='utf-8') as f:
f.__iter__()
f.__next__()
但是文件对象不只是可迭代对象,他也是迭代器对象,它可以直接调用__next__
with open('user',mode='rt',encoding='utf-8') as f:
print(f.__next__())
print(f.__next__())
而for循环的工作原理是1. dic.__iter__()得到迭代器对象
,而for循环并不知道他是文件对象,也就是说,我们按照for循环的规则进行转写
with open('user',mode='rt',encoding='utf-8') as f:
f_iter = f.__iter__() # 迭代器对象被.__iter__()转成了迭代器对象
while True:
try:
print(f_iter.__next__())
except StopIteration:
break
前文提到过迭代器对象被.__iter__()转成了迭代器对象
是不会有任何变化的,但是如果他没有这个功能,在文件对象中就会导致for循环因为不能转变为迭代器对象而导致语法错误
迭代器的优缺点
基于索引的迭代取值,所有迭代的状态都保存在了索引中,而基于迭代器实现迭代的方式不再需要索引,所有迭代的状态就保存在迭代器中,然而这种处理方式优点与缺点并存:
优点
1、为序列和非序列类型提供了一种统一的迭代取值方式。
2、惰性计算:迭代器对象表示的是一个数据流,并非是一次性全部加载到内存,它可以只在需要时才去调用next来计算出一个值,就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流,例如一个超级大的文件,python直接把文件对象做成了迭代器对象,在内存中一次性只加载next的那一部分。而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内存大小的限制,可以存放的值的个数是有限的。
缺点
1、除非取尽,否则无法获取迭代器的长度
就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流,例如一个超级大的文件,python直接把文件对象做成了迭代器对象,在内存中一次性只加载next的那一部分。而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内存大小的限制,可以存放的值的个数是有限的。
缺点
1、除非取尽,否则无法获取迭代器的长度
2、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行next方法直到值取尽,否则就会停留在某个位置,等待下一次调用next;若是要再次迭代同个对象,你只能重新调用iter方法去创建一个新的迭代器对象,如果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。