一、迭代对象
1.1、可迭代对象
从语法形式上讲;可迭代对象就是内置有.__iter__方法的对象都是可迭代对象,其中字符串,列表,字典,元组,集合,以及打开的文件都是可迭代对象
二、迭代器对象
2.1、迭代器介绍
迭代器即用来迭代取值的工具,而迭代是重复反馈过程的活动,其目的通常是为了逼近所需的目标或结果,每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值,单纯的重复并不是迭代
通过索引的方式进行迭代取值,实现简单,但仅适用于序列类型:字符串,列表,元组。对于没有索引的字典(字典是按照key取值)、集合(主要用来去重和运算)等非序列类型,必须找到一种不依赖索引来进行迭代取值的方式,这就用到了迭代器。
2.2、迭代器对象
调用obj.iter()方法返回的结果就是一个迭代器对象(Iterator)。迭代器对象是内置有iter和next方法的对象,打开的文件本身就是一个迭代器对象,执行迭代器对象.iter()方法得到的仍然是迭代器本身,而执行迭代器.next()方法就会计算出迭代器中的下一个值。 迭代器是Python提供的一种统一的、不依赖于索引的迭代取值方式,只要存在多个“值”,无论序列类型还是非序列类型都可以按照迭代器的方式取值
简单的说,就是'''可迭代对象调用__iter__方法之后就是迭代器对象了''',而是否是可迭代对象是由数据的类型决定的
2.3、易错知识总结
1、可迭代对象多次调用___iter__方法仍然是迭代器对象,
2、res=s.__iter__() # 迭代器对象
print(res.__next__()) # next其实就是迭代取值的,而且不依赖于索引取值
3、
l = [1, 2, 3, 4]
print(l.__iter__().__next__()) # 1
print(l.__iter__().__next__()) # 1
print(l.__iter__().__next__()) # 1
print(l.__iter__().__next__()) # 1
因为每次都是将可迭代对象l转换为迭代器对象然后取值,next一次就取一个值,因此每次结果为1
res = l.__iter__()
print(res.__next__()) # 1
print(res.__next__()) # 2
print(res.__next__()) # 3
print(res.__next__()) # 4
先将可迭代对象转换为迭代器对象,然后每次next就是迭代取一次值,所以结果分别为1234
三、异常捕捉
3.1、什么是异常:
异常就是错误发生的信号,如果不对该信号做处理,那么,异常发生的位置之后的代码都不能正常运行
"""
只能当你的被监测代码可能会出现,但是,又不想让它报错,这个时候使用异常捕捉是最合适的
异常捕捉的代码尽量少的写
"""
3.2、异常的信息:
1. Traceback:追踪信息,异常发生的位置
有时候错误发生的信息可能会有很多行,你就找最后一行,然后点击可以跳转到错误发生的位置
2. XXXError: 异常发生的类型
NameError KeyError ...
3. 错误发生的详细信息(最重要的:一般情况从这里就可以知道异常发生的原因)
name 'x' is not defined
3.3、异常的分类:
1. 语法错误
# 是代码运行过程中一开始就不允许的,只要语法错误,代码一定是不能够运行的
# 通过代码提示就知道语法错误的原因
2. 逻辑错误
# 在编码过程中,逻辑错误是允许出现的
但是我们应该尽量避免逻辑错误出现的可能性
3.4、异常如何进行捕捉
异常处理的基本形式:
方式一:
try:
被检测的代码块
except 异常类型:
检测到异常,就执行这个位置的逻辑
方式二:
如果我们想分别用不同的逻辑处理,需要用到多分支的except(类似于多分支的elif,从上到下依次匹配,匹配成功一次便不再匹配其他)
try:
被检测的代码块
except NameError:
触发NameError时对应的处理逻辑
except IndexError:
触发IndexError时对应的处理逻辑
except KeyError:
触发KeyError时对应的处理逻辑
方式三:
如果我们想多种类型的异常统一用一种逻辑处理,可以将多个异常放到一个元组内,用一个except匹配
try:
被检测的代码块
except (NameError,IndexError,TypeError):
触发NameError或IndexError或TypeError时对应的处理逻辑
方式四:
如果我们想捕获所有异常并用一种逻辑处理,Python提供了一个万能异常类型Exception
try:
被检测的代码块
except Exception:
所有类型的异常统一用此处的逻辑处理
方式五:
try还可以与finally连用,从语法上讲finally必须放到else之后,但可以使用try-except-finally的形式,也可以直接使用try-finally的形式。无论被检测的代码块是否触发异常,都会执行finally的子代码块,因此通常在finally的子代码块做一些回收资源的操作,比如关闭打开的文件、关闭数据库连接等
try:
被检测的代码块
except 异常类型1:
pass
except 异常类型2:
pass
......
else:
没有异常发生时执行的代码块
finally:
无论有无异常发生都会执行的代码块
四、for循环的原理
for循环又称为迭代循环,in后跟的为任意可迭代对象,for循环工作的时候,会先调用iter方法,将可迭代对象转化为迭代器对象,然后在调用该迭代器对象的next方法,将取到的值一个一个赋值给for后边的变量名,直到将可迭代对象内的值取完,捕捉到StopIteration异常,结束迭代。
代码逻辑如下:
goods=['mac','lenovo','acer','dell','sony']
i=iter(goods) #每次都需要重新获取一个迭代器对象
while True:
try:
print(next(i))
except StopIteration: #捕捉异常终止循环
break
五、迭代器的优缺点
基于索引的迭代取值,所有迭代的状态都保存在了索引中,而基于迭代器实现迭代的方式不再需要索引,所有迭代的状态就保存在迭代器中,然而这种处理方式优点与缺点并存:
5.1 优点:
1、为序列和非序列类型提供了一种统一的迭代取值方式。
2、惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用next来计算出一个值,就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内存大小的限制,可以存放的值的个数是有限的。
5.2 缺点:
1、除非取尽,否则无法获取迭代器的长度
2、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行next方法直到值取尽,否则就会停留在某个位置,等待下一次调用next;若是要再次迭代同个对象,你只能重新调用iter方法去创建一个新的迭代器对象,如果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值,原因如下:
在Python中,迭代器(iterator)是一个可以记住遍历的位置的对象。迭代器从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
当你使用同一个迭代器在多个循环中时,迭代器的状态(即当前位置)会在每个循环中被改变。由于迭代器只能前进,并且一旦所有元素被遍历后就会耗尽,所以如果一个迭代器已经在之前的循环中被完全遍历,那么后续的循环将无法从中获取任何值。举个例子:
my_list = [1, 2, 3]
my_iterator = iter(my_list)
# 第一个循环
for item in my_iterator:
print(item) # 输出: 1, 2, 3
# 第二个循环尝试使用同一个迭代器
for item in my_iterator:
print(item) # 不会有输出,因为迭代器已经耗尽
在上面的例子中,第一个循环遍历了整个列表,并将迭代器移动到了列表的末尾。当第二个循环尝试使用时,迭代器已经耗尽,因此没有输出。
如果你希望多个循环能够遍历同一个集合,你应该为每个循环创建新的迭代器。例如,可以通过重新调用iter()
函数或创建集合的新副本(如列表的切片)来实现这一点。
my_list = [1, 2, 3]
# 为每个循环创建新的迭代器
for item in iter(my_list):
print(item) # 输出: 1, 2, 3
for item in iter(my_list):
print(item) # 再次输出: 1, 2, 3
# 或者使用列表的切片来创建新的可迭代对象
for item in my_list[:]:
print(item) # 输出: 1, 2, 3
for item in my_list[:]:
print(item) # 再次输出: 1, 2, 3
注意,对于大型集合或性能敏感的代码,每次都创建新的迭代器或副本可能会带来额外的开销。在这种情况下,应该仔细考虑你的需求,并可能寻找其他解决方案,比如使用生成器函数来按需生成值。