函数篇:可迭代及迭代器对象、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、迭代取值

优势:兼容所有容器类型

劣势:永远是顺序取值,且无法重复获取,取完就结束

注意:迭代器里面的东西,取一个少一个,取完就没了

posted @   马氵寿  阅读(220)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开