# Python教程 - 10 异常
# 10.1 异常的概念
什么是异常?
异常也就是程序执运行时发生的错误。
例如:我们执行 1除以0,0是不能作为除数的。
print(1 / 0)
执行的时候就会发生错误:
例如,以读的方式打开一个不存在的文件
file = open("test.txt", "r")
执行的时候也会报错:
当程序出现错误的时候,我们通常称之为:抛出异常。
程序抛出异常就无法继续执行了,但是任何程序都不可能是完美的没有bug的,只能尽可能的对错误进行预防和处理。
# 10.2 捕获异常
对异常进行预防和提前处理,这种行为通常称之为异常捕获。
一个程序出现任何错误,就停止运行肯定不是我们希望看到的,即使程序出现错误,哪怕给与用户一个错误提示,也是一种积极的处理方式。
# 1 异常捕获语法
最简单的异常捕获语法:
try:
可能出现错误的代码
except [Exception as e]:
出现错误后的处理
2
3
4
举个栗子:
try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
except:
print("请输入正确的数字!")
2
3
4
5
6
执行结果:
请输入数字:abc 请输入正确的数字!
这样在发生错误的时候,可以给用户一个提示。当然具体的处理方式需要根据业务需求的处理,这里只是举个例子。
如果想获取异常的信息,可以使用 except Exception as e:
:
try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
except Exception as e:
print(e)
print("请输入正确的数字!")
2
3
4
5
6
7
# 2 捕获指定类型的异常
在程序运行时,可能会出现不同类型的异常,可能需要对不同类型的异常进行不同的处理,这个时候就需要根据类型来捕获异常了。
语法:
try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
except ValueError as e:
# 针对ValueError错误进行处理
print(e)
print("请输入正确的数字!")
2
3
4
5
6
7
8
上面捕获了ValueError类型的异常,在捕获异常后,打印了异常信息和提示信息。
如果不想获取异常信息,还可以省略 as
:
try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
except ValueError:
print("请输入正确的数字!")
2
3
4
5
6
需要注意,上面只是捕获了ValueError类型的异常,如果有代码抛出了其他类型的异常,是无法捕获的。
例如,我们修改代码如下:
try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
print(12 / num)
except ValueError:
print("请输入正确的数字!")
2
3
4
5
6
7
当输入0的时候,执行结果:
无法捕获除数为0的异常ZeroDivisionError。
# 3 捕获多个异常
我们可以同时捕获多个异常,并对。
语法:
try:
# 可能出现错误的代码
except 错误类型1:
# 捕获错误类型1
except 错误类型2:
# 捕获错误类型2
except (错误类型3, 错误类型4):
# 可以同时捕获错误类型3和错误类型4
except Exception as e:
# 捕获其他类型
print("未知错误:%s" % e)
2
3
4
5
6
7
8
9
10
11
如果真多多种异常类型采用相同的处理可以使用 except (错误类型3, 错误类型4):
这样的捕获方式。
如果我们不能判断是否还会抛出其他类型的错误,可以在最后添加 except Exception as e:
用来捕获其他类型的错误。
举个栗子:
try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
print(12 / num)
except ValueError:
print("请输入正确的数字!")
except ZeroDivisionError:
print("除数不能为0")
except Exception as e:
# 捕获其他类型
print("未知错误:%s" % e)
2
3
4
5
6
7
8
9
10
11
12
# 4 异常else
else表示的是如果没有发生异常要执行的代码。
语法:
try:
# 可能出现错误的代码
except Exception as e:
# 捕获异常
else:
# 没有发生异常要执行的代码
2
3
4
5
6
举个栗子:
try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
print(12 / num)
except Exception as e:
print(e)
else:
print("没有发生异常")
2
3
4
5
6
7
8
9
输入12,执行结果:
请输入数字:12 1.0 没有发生异常
输入0,执行结果:
请输入数字:0 division by zero
# 5 异常finally
finally表示的是无论是否发生异常都要执行的代码。
举个栗子:
try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
print(12 / num)
except Exception as e:
print(e)
finally:
print("执行完成")
2
3
4
5
6
7
8
9
当输入0的时候,执行结果:
请输入数字:0 division by zero 执行完成
finally一般在文件操作的时候用的比较多,就是不管是否发生错误都要关闭文件。
举个栗子:
try:
f = open('test.txt', 'r')
except Exception as e:
print(e)
finally:
try:
f.close()
except Exception as e:
print("关闭文件出错:%s" % e)
2
3
4
5
6
7
8
9
因为关闭文件也很有可能报错,所以在关闭文件的时候,也进行了异常捕获
# 6 异常捕获的完整语法
将之前的异常捕获、else 和 finally 都添加上,完整的异常捕获代码如下:
try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
print(12 / num)
except ValueError:
print("请输入正确的整数!")
except ZeroDivisionError:
print("除数不能为0")
except Exception as result:
print("未知错误:%s" % result)
else:
print("程序正常执行")
finally:
print("执行完成")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
当然也不是所有的模块都需要,按照自己需要使用即可。
# 10.3 异常的传递
什么是异常的传递?
异常的传递,就是当函数或方法执行的时候出现异常,如果没有进行捕获,就会将异常传递给该函数或方法的调用者,如果调用者仍未处理异常,则继续向上传递,直到传递给主程序,如果主程序仍然没有进行异常处理,则程序将被终止。
举个栗子:
def input_fun():
# 提示用户输入一个数字
num = int(input("请输入数字:"))
print(12 / num)
def call_fun(): # 调用上面的input_fun()
input_fun()
def main(): # 调用上面的call_fun()
try:
call_fun()
except Exception as e:
print(e)
main()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
上面代码,执行的时候输入0,会在input_fun() 函数中抛出异常,因为input_fun()函数中没有进行异常处理,则异常会抛给call_fun()函数,call_fun()没有进行异常处理,则抛给main()函数,main()函数中则对异常进行了处理,程序不会崩溃。
利用异常的传递性,如果我们在main()函数中进行了异常捕获,无论程序哪里发生了错误,最终都会被传递到main()函数中,保证所有的异常都会被捕获。
# 10.4 主动抛出异常
除了代码执行的时候出错,Python解释器会抛出异常,我们还可以根据实际的业务需要,主动抛出异常。
首先创建一个异常对象,然后使用 raise 关键字抛出异常对象。
举个栗子:
def input_username():
# 输入密码
name = input("请输入用户名:")
if len(name) > 16 or len(name) < 8:
raise Exception("用户名格式错误") # 主动抛出异常
return name
try:
password = input_username()
print(f"输入的用户名:{password}")
except Exception as e:
print("用户名格式错误,请重新输入")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
当输入abc的时候,执行结果:
请输入用户名:abc 用户名格式错误,请重新输入
# 10.5 自定义异常类
上面我们使用了Python中存在的异常类,我们还可以自定义异常类。
自定义异常类需要继承Exception类,类名建议以Error结尾。
举个栗子:
下面我们首先自定义了两个异常类,然后根据需要抛出这两个异常类的对象,在捕获异常的时候,根据异常类型进行不同的处理。
并在自定义异常类中可以封装自定义的参数。
class UsernameEmptyError(Exception): # 自定义异常类
pass
class UsernameLengthError(Exception): # 自定义异常类
def __init__(self, message, min_length, max_length):
super().__init__(self, message) # 推荐先调用父类的方法
self.min_length = min_length # 可以封装自定义的信息
self.max_length = max_length
def input_username():
# 输入密码
name = input("请输入用户名:")
if len(name) < 1:
raise UsernameEmptyError() # 抛出异常
if len(name) > 16 or len(name) < 8:
raise UsernameLengthError("用户名格式错误", 8, 16) # 抛出异常
return name
try:
password = input_username()
print(f"输入的用户名:{password}")
except UsernameEmptyError:
print("用户名不能为空")
except UsernameLengthError as e:
print(f"用户名长度为{e.min_length}~{e.max_length}个字符") # 可以获取异常中的信息
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
yz