函数篇:可迭代及迭代器对象、for循环的原理和本质、异常处理
2022.3.23学习笔记
- 可迭代对象
- 迭代器对象
- for循环的内部原理
- 异常处理
- for循环的本质(代码模拟)
- 迭代取值与索引取值的对比
一、可迭代对象
1、迭代的概念
迭代就是更新换代,每一次迭代都是基于上一次的结果,类似游戏版本的更新
特征:每一次的结果都不一样或者都有更新才叫做迭代
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
while True:
print(123) # 单纯的循环不叫迭代
n = 1
while True:
print(n)
n +=1 # 每次都不一样的,这样的循环才属于迭代
# 迭代取值
l1 = [11, 22, 33, 44, 55]
n = 0
while n < len(l1):
print(len(n))
n += 1
2、可迭代对象
内置有__iter__方法的都可成为可迭代对象,一般支持for循环的都是可迭代对象,如字符串、列表、字典、元组、集合、文件对象等
复制代码
- 1
- 2
- 3
- 4
# 可迭代对象可以用.__iter__的方式进行调用
# __iter__读法:双下iter方法,不要读杠杠
s = 'jsdon'
s.__iter__
二、迭代器对象
1、概念
复制代码
- 1
可迭代对象调用__iter__方法后生成的结果就是迭代器对象
2、特征
复制代码
- 1
- 2
- 3
- 4
含有__iter__()方法和__next__()方法
s = 'jsdon'
res = s.__iter__() # 将s转为迭代器对象
res.__next__() # 使用next调用res这个迭代器对象,表示取第一个元素
3、迭代器对象的理解
作用:极大节省存储空间
类似哆啦a梦的口袋,不用的时候就是一个口袋,用的时候可以拿出很多东西
4、迭代器对象如何取值
复制代码
- 1
- 2
- 3
调用__next__()方法即可
注意:如果数据取完了则会直接报错!!!
# for循环的底层依据就是迭代器对象
5、迭代器对象补充说明
(1)转化及调用的简易写法
复制代码
- 1
- 2
- 3
__iter__() == iter()
__next__() == next()
(2)文件对象
文件对象本身就是迭代器对象,无须使用iter()调用
(3)多次调用iter()的结果
可迭代对象调用1次iter()会转化成迭代器对象,如果继续调用结果还是迭代器对象本身
复制代码
- 1
- 2
- 3
res = s.__iter__()
res1 = s.__iter__().__iter__().__iter__()
res == res1 # 两个迭代器对象是一样的
(4)迭代取值的要求
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
s = 'jason'
print(s.__iter__().__next__()) # j
print(s.__iter__().__next__()) # j
print(s.__iter__().__next__()) # j
print(s.__iter__().__next__()) # j
print(s.__iter__().__next__()) # j
思考?为什么以上调用了5次结果一样呢?
这是因为s转化成迭代器对象之后需要一个变量名接收,不然再次调用还是从头开始的,因此要这样使用
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
s = 'jason'
res = s.__iter__()
print(res.__next__()) # j
print(res.__next__()) # a
print(res.__next__()) # s
print(res.__next__()) # o
print(res.__next__()) # n
三、for循环内部原理
先看for循环的结构:
for 变量名 in 可迭代对象:
循环体代码
经过之前的学习,in后面相当于是将可迭代对象iter()一下变成迭代器对象,然后循环取里面的值,直到取完
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
l1 = [1, 2, 3, 4, 5, 6, 7]
res = l1.__iter__()
n = 0
while n < len(l1):
print(res.__next__())
n +=1 # 这里如果不进行人为计数,就会出现异常,也就是报错
所以什么是异常处理呢?
四、异常处理
1、什么是异常
代码运行出错之后就是异常,异常会导致程序立刻停止,因此我们再编写代码的时候需要极力避免(异常的外号叫:bug)
2、异常信息的组成部分(报错)
先看下面这个报错信息
复制代码
- 1
- 2
- 3
- 4
1.Traceback (most recent call last):
2. File "/Users/jiboyuan/PycharmProjects/day16/05 异常处理.py", line 3, in <module>
name
3.NameError: name 'name' is not defined
序号1:line关键字所在的一行,用于提示哪一行报错,点击蓝色字体即可定位,如果报错信息很长,那么一般最后一个才是
序号2:NameError是错误的类型中的一种,还有很多其他类型
序号3:name 'name' is not defined是具体的报错原因,一般也是解决报错的方法
3、异常的分类
1.语法异常
最基础的语法结构,出现了会立即报错,pycharm中会出现红色波浪线
2.逻辑异常
不会直接报错,语法里有这个操作,但是乱用,比如:
复制代码
- 1
- 2
name = 'jason'
name.append() # 不会立马报错,但是字符串不可以使用列表内置方法,运行程序便会报错
如果逻辑异常,尽快修改即可
4、异常类型
举几个例子,如下
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
1.print(name) # NameError 名字错误
2.l1 = [1, 2, 3]
print(l1[100]) # IndexError 索引错误
3.dict = {'name':'jason'}
print(dict['age']) # KeyError 键错误
4.int('jason') # ValueError 值错误
......
还有很多异常的类型,需要再改bug的时候多熟悉
五、异常处理实操
1、基本语法结构
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
try:
可能会出错的代码
except 错误类型1 as e: # e指代错误提示信息
针对1的处理措施
except 错误类型2 as e:
针对2的处理措施
except 错误类型3 as e:
针对3的处理措施
...
2、统一错误类型处理
由于有时候自己也不清楚是什么bug,使用下面的语法结构,可以对任意错误类型进行操作,也叫“万能异常”
复制代码
- 1
- 2
- 3
- 4
try:
可能会出错的代码
except Exception as e:
统一的额处理措施
但是并不是以后都可以用这种方式,因为异常处理的准则就是被检测的代码越少越好,尽量少用
3、异常处理了解篇
(1)结合else使用
try检测的代码没有发生异常,正常运行完毕后会执行else的子代码
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
try: # 检测代码有无异常
可能会出错的代码
except Exception as e: # 检测异常走except子代码
统一的处理措施
else: # 检测无异常走else子代码
可能会出错的代码没有出错 最后走else子代码
(2)结合finally使用
无论try检测的代码有无异常最后都会执行finally子代码
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
try: # 检测代码有无异常
可能会出错的代码
except Exception as e: # 检测异常走except子代码
统一的处理措施
else: # 检测无异常走else子代码
可能会出错的代码没有出错 最后走else子代码
finally: ####### 最后一步都会走finally
无论上面有没有异常,最后都会走这一步
(3)assert断言
复制代码
- 1
- 2
name = 'jaosn'
assert isinstance(name,str)
(4)raise主动报错(需要掌握)
复制代码
- 1
- 2
raise NameError('就是要报错,怎么了')
因为是主动报错,所以明确知道是什么错误
六、for循环的本质
了解了异常处理的一些知识,我们再回到for循环的原理上
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
l1 = [1, 2, 3, 4, 5, 6, 7]
res = l1.__iter__()
n = 0
while n < len(l1):
print(res.__next__())
n +=1 # 这里如果不进行人为计数,就会出现异常,也就是报错
刚才说了,这里如果不写n += 1,数据取完后就会报错,结合异常处理的知识我们可以这样做:
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
l1 = [1, 2, 3, 4, 5, 6, 7]
res = l1.__iter__()
while True:
try:
print(res.__next__()) # 检测这一行代码
except StopIteration as e: # 运行时了解到报错StopIteration
break # 报错直接退出循环
这样在我们知道报错的类型就可以加以相应的处理措施啦,是不是很神奇
七、迭代取值与索引取值的对比
1、索引取值
优势:可以反复取相同元素,没有固定方向
劣势:只能支持有序的容器类型,且兼容性没有迭代取值高
2、迭代取值
优势:兼容所有容器类型
劣势:永远是顺序取值,且无法重复获取,取完就结束
注意:迭代器里面的东西,取一个少一个,取完就没了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步