Python迭代器和异常处理
迭代器和异常处理
1. 可迭代对象
迭代:迭代即更新换代,每次的更新都必须依赖于上一次的结果.
每一次对过程的重复称为一次“迭代”,每一次迭代得到的结果会被用来作为下一次迭代的初始值.
在python
中可以通过for
循环来遍历这个list
或tuple
,这种遍历我们称为迭代(Iteration)。
迭代其实给我们提供了一种不依赖索引取值的方式
1.1 可迭代对象
内置有__iter__
方法的都称之为可迭代对象.
针对双下滑线开头 双下滑线结尾的方法 最为专业标准的读法为
双下 方法名
查找哪些类型是可迭代对象
# 1. 整型:
>>> i = 1
>>> i.__iter__() # 没有__iter__方法,不是可迭代对象
AttributeError: 'int' object has no attribute '__iter__'
# 2. 浮点型:
>>> f=1.2
>>> f.__iter__() # 没有__iter__方法,不是可迭代对象
AttributeError: 'float' object has no attribute '__iter__'
# 3. 布尔值
>>> bl = True
>>> bl.__iter__() # 没有__iter__方法,不是可迭代对象
AttributeError: 'bool' object has no attribute '__iter__'
# 4. 字符串
>>> str = 'abc'
>>> str.__iter__() # 有__iter__方法,是可迭代对象
<str_iterator object at 0x7ffa1ac28898>
# 5. 列表
>>> list1 = [1, 2, 3, 4]
>>> list1.__iter__() # 有__iter__方法,是可迭代对象
<list_iterator object at 0x7ffa1ac28860>
# 6. 元组
>>> tuple1 = (1, 2, 3, 4)
>>> tuple1.__iter__() # 有__iter__方法,是可迭代对象
<tuple_iterator object at 0x7ffa1ac286d8>
# 7. 字典
>>> dict1 = {'name':'hans', 'age':18}
>>> dict1.__iter__() # 有__iter__方法,是可迭代对象
<dict_keyiterator object at 0x7ffa1ac2e7c8>
# 8. 集合
>>> set1 = {1, 2, 3, 4}
>>> set1.__iter__() # 有__iter__方法,是可迭代对象
<set_iterator object at 0x7ffa1ac0fc60>
# 9. 文件对象
>>> f = open(r'a.txt', 'r', encoding='utf8')
>>> f.__iter__()
<_io.TextIOWrapper name='a.txt' mode='r' encoding='utf8'>
可迭代对象为:字符串、列表、元组、字典、集合、文件对象
其实__iter__()
有更简单的调用方式:iter()
# 列表:
>>> list1 = [1, 2, 3, 4]
>>> list1.__iter__()
<list_iterator object at 0x7ffa1ac28860>
>>> iter(list1)
<list_iterator object at 0x7ffa1ac28400>
# 字典:
>>> dict1 = {'name':'hans', 'age':18}
>>> dict1.__iter__() # 有__iter__方法,是可迭代对象
<dict_keyiterator object at 0x7ffa1ac2e7c8>
>>> iter(dict1)
<dict_keyiterator object at 0x7ffa1ac2e7c8>
# __iter__()和iter()返回的结果是一样的。
2.迭代器对象
现在我们知道可迭代对象为:字符串、列表、元组、字典、集合、文件对象
但这些是可迭代对象,但不一定是迭代器对象。
因为,迭代器是:即含有__iter__
方法又含有__next__
方法的才叫迭代器。
查看可迭代对象是否为迭代器:
# 1. 字符串
>>> str = 'abc'
>>> str.__next__() # 没有__next__方法,不是迭代器
AttributeError: 'str' object has no attribute '__next__'
# 2. 列表
>>> list1 = [1, 2, 3, 4]
>>> list1.__next__() # 没有__next__方法,不是迭代器
AttributeError: 'list' object has no attribute '__next__'
# 3. 元组
>>> tuple1 = (1, 2, 3, 4)
>>> tuple1.__next__() # 没有__next__方法,不是迭代器
AttributeError: 'tuple' object has no attribute '__next__'
# 4. 字典
>>> dict1 = {'name':'hans', 'age':18}
>>> dict1.__next__() # 没有__next__方法,不是迭代器
AttributeError: 'dict' object has no attribute '__next__'
# 5. 集合
>>> set1 = {1, 2, 3, 4}
>>> set1.__next__() # 没有__next__方法,不是迭代器
AttributeError: 'set' object has no attribute '__next__'
# 6. 文件对象
>>> f = open(r'a.txt', 'r', encoding='utf8')
>>> f.__next__() # 有__next__方法,是迭代器
'Hello\n'
可迭代对象字符串、列表、元组、字典、集合、文件对象中只有文件对象是迭代器。
文件对象
本身即是可迭代对象
又是迭代器对象
虽然字符串、列表、元组、字典、集合不是迭代器,但是可以使用__iter__
方法转换成迭代器
# 1. 字符串
>>> str = 'abc'
>>> s = str.__iter__() # 将迭代对象使用__iter__方法转成迭代器对象
>>> s.__iter__() # s即有__iter__方法又有__next__方法
<str_iterator object at 0x7ffa1ac28c18>
>>> s.__next__() # 迭代器对象执行__next__方法其实就是在迭代取值(类似for循环)
'a'
>>> s.__next__()
'b'
>>> s.__next__()
'c'
# 2. 列表
>>> list1 = [1, 2, 3, 4]
>>> l = list1.__iter__()
>>> l.__next__()
1
>>> l.__next__()
2
>>> l.__next__()
3
>>> l.__next__()
4
>>> l.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
# 同理元组、字典、集合都可以使用这种方法转成迭代器。
迭代器对象执行__next__
方法其实就是在迭代取值,但是它不知道序列的长度,所以会__next__
会一直取,直到没有数据时抛出StopIteration
错误。
迭代器对象无论执行多少次__iter__方法,还是迭代器对象(本身)
>>> list1 = [1, 2, 3, 4]
>>> l = list1.__iter__()
>>> l.__iter__()
<list_iterator object at 0x7ffa1ac286a0>
>>> l.__iter__().__iter__().__iter__()
<list_iterator object at 0x7ffa1ac286a0>
# 执行一次__iter__方法和多次执行__iter__方法,结果一样。
练习
>>> dict1 = {'name':'hans', 'age':18}
>>> d = dict1.__iter__()
>>> d.__iter__().__next__() # 猜一下运行结果
'name'
>>> d.__iter__().__next__()
'age'
# 上面就是体现了,迭代器对象无论执行多少次__iter__方法,还是迭代器对象(本身)
>>> dict1.__iter__().__next__()
>>> dict1.__iter__().__next__()
>>> dict1.__iter__().__next__()
# 猜一下上面执行的结果
# 结果:
'name'
'name'
'name'
# 每一次都会重新转换成迭代器。然后进行一次迭代取值
3. for循环本质
循环打印出列表中每个元素, 但是不能使用for
循环
l1 = [1,2,3,4,5,6,7,8,9,11,22,33,44,55]
res = l1.__iter__()
count = 0
while count < len(l1):
print(res.__next__())
count +=1
# 执行结果:
1
2
3
4
5
6
7
8
9
11
22
33
44
55
其实for
循环本质就是使用迭代器
for循环内部原理
1. 将关键字`in`后面的数据先调用`__iter__`方法转为迭代器对象
2. 循环执行`__next__`方法
3. 取完之后`__next__`会报错 但是`for`循环会自动捕获该错误并处理
4. 异常处理
4.1 什么是异常
代码运行出错会导致异常 异常发生后如果没有解决方案则会到底整个程序结束
4.2 异常三个重要组成部分
-
traceback
翻到最下面从下往上的第一个蓝色字体鼠标左键点击即可跳转到错误的代码所在的行 -
xxxError
异常的类型
-
异常类型冒号后面的内容
异常的详细原因(仔细看完之后可能就会找到解决的方法)
4.3 异常的种类
- 语法错误
- 逻辑错误
4.4 常见异常类型
异常 | 描述 |
---|---|
AssertionError | assert(断言)语句失败 |
AttributeError | 试图访问一个对象没有的属性,比如foo.x ,但是foo没有x这个属性。 |
IOError | 输入/输出异常,基本上是无法打开文件。 |
ImportError | 无法引入模块或者包,基本上是路径问题 |
IndentationError | 语法错误,代码没有正确对齐 |
IndexError | 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] |
KeyError | 试图访问字典里不存在的键 |
KerboardInterrupt | Ctrl + C 被按下 |
NameError | 使用一个还未被赋值予对象的变量 |
SyntaxError | Python代码非法,代码不能解释 |
TypeError | 传入对象类型与要求的不符 |
UnboundLocalError | 试图访问一个还未被设置的局部变量,基本上是由于另一个同名的全局变量,导致你以为正在访问它 |
ValueError | 传入一个调用者不期望的值,即使值的类型是正确的 |
[Python官方异常列表:][https://docs.python.org/3/library/exceptions.html#exception-hierarchy ]
https://docs.python.org/3/library/exceptions.html#exception-hierarchy
4.5 异常处理基本语法结构
格式:
try:
有可能会出错的代码
except 异常类型 as e:
出错之后对应的处理机制(e是错误的详细信息)
except 异常类型 as e:
出错之后对应的处理机制(e是错误的详细信息)
except 异常类型 as e:
出错之后对应的处理机制(e是错误的详细信息)
示例:
# 代码:
f = open(r'b.txt', 'r', encoding='utf8')
print("Hello")
# 执行结果:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'b.txt'
# 没有b.txt这个文件,程序报错退出,后面的print("Hello")没有执行
# 使用异常处理
# 代码:
try:
f = open(r'b.txt', 'r', encoding='utf8')
except FileNotFoundError as e:
print("没有这个文件,请先创建它")
print("Hello")
# 执行结果:
没有这个文件,请先创建它
Hello
# 虽然也报错,但是捕获到异常,所以程序没有报错退出print()依然执行
使用异常处理时,except
可以写多个,写多个会比较麻烦,可以使用Exception
它能捕获各种异常
# 代码:
try:
f = open(r'b.txt', 'r', encoding='utf8')
except Exception as e: # 使用Exception依然能捕获到异常
print("没有这个文件,请先创建它")
# 执行结果:
没有这个文件,请先创建它
使用异常处理,循环打印出列表中每个元素, 但是不使用for
循环
l1 = [1,2,3,4,5,6,7,8,9,11,22,33,44,55]
def foo(l):
res = l.__iter__()
while True:
try:
print(res.__next__())
except StopIteration as e:
break
foo(l1)
4.5.2 异常使用else
异常也可以使用else
# 代码1:
list_num = [1, 2, 3, 4, 5]
try:
print(list_num[10]) # 超过索引范围
except IndexError as e:
print("Not fond")
else:
print("no error")
# 执行结果:
Not fond
# 发现没有执行else里面的代码。
# 代码2:
list_num = [1, 2, 3, 4, 5]
try:
print(list_num[1]) # 没有超过索引范围
except IndexError as e:
print("Not fond")
else:
print("no error")
# 执行结果:
2
no error
# 执行了 else
所以else
是被监测的代码不报错的时候执行。
4.5.3 异常使用finally
finally
是无论被监测的代码是否报错最终都会执行
# 有异常,代码
list_num = [1, 2, 3, 4, 5]
try:
print(list_num[10]) # 超过索引范围
except IndexError as e:
print("Not fond")
else:
print("no error")
finally:
print("都会执行")
# 执行结果:
Not fond
都会执行
# 无异常,代码:
list_num = [1, 2, 3, 4, 5]
try:
print(list_num[1])
except IndexError as e:
print("Not fond")
else:
print("no error")
finally:
print("都会执行")
# 执行结果:
2
no error
都会执行
4.5.4 断言assert
断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况。
用于判断一个表达式,在表达式条件为 false
的时候触发异常。
>>> 1 == 1
True
>>> 1 == 2 # 为假则返回False
False
>>> assert 1 == 1
>>> assert 1 == 2 # 使用assert为假则报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
4.5.5 自定义异常raise
raise 语句抛出一个指定的异常。
raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类
>>> x = 5
>>> if x > 4:
... raise Exception('%s 不能大于4' % x)
raceback (most recent call last):
File "<stdin>", line 2, in <module>
Exception: 5 不能大于4
>>> if x > 4:
raise IndexError ('%s 不能大于4' % x)
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
IndexError: 5 不能大于4
raise
的错误类型可以自己定义,或者直接写Exception
, 这个自己定义的类型要在python
错误类型中。
4.5.6 示例:用while
使用迭代器模拟for
list_num = [1, 2, 3, 4, 5]
res = list_num.__iter__()
while True:
try:
print(res.__next__())
except StopIteration as e:
break
4.5.7 自定义异常
我们自己可以通过创建新的异常类命名自己的异常,论是以直接还是间接的方式,异常都应从 Exception
类派生,也有从BaseException
类派生,BaseException
是为系统退出异常而保留的,而且Exception
是继承了BaseException`.
class MyError(Exception):
def __init__(self,msg):
self.msg = msg
def __str__(self):
return self.msg
raise MyError("出现了异常")
# 执行:
Traceback (most recent call last):
raise MyError("出现了异常")
__main__.MyError: 出现了异常
# class MyError(BaseException):
4.6 使用异常处理规则
- 有可能会出现错误的代码才需要被监测
- 被监测的代码一定要越少越好
- 异常捕获使用频率越低越好