21.python迭代器

迭代器指的是迭代取值的工具,迭代指的是一个重复的过程,每一次重复都是基于上一次的结果而继续,单纯的重复并不是迭代

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__方法将可迭代对象转换为迭代器对象

image-20201224162118990

可迭代对象:字符串、列表、元组、字典、集合、文件对象

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__())

image-20201225170845690

而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方法去创建一个新的迭代器对象,如果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值