Python异常处理
异常分类
程序中难免出现错误,总共可分为两种。
1.逻辑错误
2.语法错误
对于刚接触编程的人来说,这两个错误都会经常去犯,但是随着经验慢慢的基类,语法错误的情况会越来越少反而逻辑错误的情况会越来越多(因为工程量巨大)。不论多么老道的程序员都不可避免出现这两种错误。
异常的三大信息
常用的异常类
在Python中一切皆对象,异常本身也是由一个类生成的,NameError
其实本身就是一个异常类,其他诸如此类的异常类还有很多。
Python中常见的异常类 | |
---|---|
AttributeError | 试图访问一个对象没有的属性,比如foo.x ,但是foo 并没有属性x |
IOError | 输入/输出异常;基本上是无法打开文件 |
ImportError | 无法引入模块或包;基本上是路径问题或名称错误 |
IndentationError | 语法错误(的子类) ;代码没有正确对齐 |
IndexError | 下标索引超出序列边界,比如当x 只有三个元素,却试图访问x[5] |
KeyError | 试图访问字典里不存在的键 |
KeyboardInterrupt | Ctrl+C 被按下 |
NameError | 试图使用一个还未被赋予对象的变量 |
SyntaxError | Python代码非法,代码不能编译(其实就是语法错误,写错了) |
TypeError | 传入对象类型与要求的不符合 |
UnboundLocalError | 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的 全局变量,导致你以为正在访问它 |
ValueError | 传入一个调用者不期望的值,即使值的类型是正确的 |
异常处理
我们可以来用某些方法进行异常捕捉,当出现异常时我们希望代码以另一种逻辑运行,使得我们的程序更加健壮,这个就叫做异常处理。异常处理是非常重要的,本身也并不复杂,千万不可马虎大意。
但是切记不可滥用异常处理,这会使得你的代码可读性变差。
if else处理异常
if
和else
本身就具有处理异常的功能,他们更多的是在我们能预测到可能出现的范围内进行规避异常,对于我们不能预测的异常来说就显得不是那么的好用。如下:
# ==== if else 处理异常 ==== while 1: select = input("请输入数字0进行关机:").strip() if select.isdigit(): # 我们可以防止用户输入非数字的字符 if select == "0": print("正在关机...") break print("输入有误,请重新输入") # ==== 执行结果 ==== """ 请输入数字0进行关机:关机 输入有误,请重新输入 请输入数字0进行关机:关机啊 输入有误,请重新输入 请输入数字0进行关机:0 正在关机... """
这种异常处理机制虽然非常简单,但是并不灵活,我们可以使用更加简单的方式来处理他们。
try except处理异常
try
:代表要检测可能出现异常的代码块
except
:当异常出现后的处理情况
执行流程:
try
中检测的代码块 —> 如果有异常 —> 执行except
代码块 —> 执行正常逻辑代码 —> 程序结束
try
中检测的代码块 —> 如果没有异常 —> 执行完毕try
中的代码块 —> 执行正常逻辑代码 —> 程序结束
# ==== try except 执行流程 有异常的情况 ==== li = [1,2,3] try: print("开始执行我try了...") print(li[10]) # 出错点... print("继续执行我try...") except IndexError as e: print("有异常执行我except...") print("正常逻辑代码...") # ==== 执行结果 ==== """ 开始执行我try了... 有异常执行我except... 正常逻辑代码... """
# ==== try except 执行流程 无异常的情况 ==== li = [1,2,3] try: print("开始执行我try了...") print(li[2]) print("继续执行我try...") except IndexError as e: print("有异常执行我except...") print("正常逻辑代码...") # ==== 执行结果 ==== """ 开始执行我try了... 3 继续执行我try... 正常逻辑代码... """
# ==== try except 处理异常 ==== while 1: try: # try检测可能出错的语句,一旦出错立马跳转到except语句块执行代码。 select = int(input("请输入数字0进行关机:").strip()) if select == 0: print("正在关机...") break print("输入有误,请重新输入...") except ValueError as e: # 当执行完except的代码块后,程序运行结束,其中e代表的是异常信息。 print("错误信息是:",e) print("输入有误,请重新输入") # ==== 执行结果 ==== """ 请输入数字0进行关机:1 输入有误,请重新输入... 请输入数字0进行关机:tt 错误信息是: invalid literal for int() with base 10: 'tt' 输入有误,请重新输入 请输入数字0进行关机:0 正在关机... """
多段except捕捉多异常
我们可以使用try
和多段except
的语法来检测某一代码块,可以更加方便的应对更多类型的错误,Ps不常用:
# ==== 多段 except 捕捉多异常 ==== while 1: li = [1,2,3,4] dic = {"name":"Yunya","age":18} li_index = input("请输入索引:") dic_key = input("请输入键的名称:") if li_index.isdigit(): li_index = int(li_index) try: print(li[li_index]) print(dic[dic_key]) except IndexError as e1: # 注意,先抛出的错误会直接跳到其处理的except代码块,而try下面的语句将不会被执行。 print("索引出错啦!") except KeyError as e2: print("键出错啦!") # ==== 执行结果 ==== """ 请输入索引:10 请输入键的名称:gender 索引出错啦! 请输入索引:2 请输入键的名称:gender 3 键出错啦! """
元组捕捉多异常
使用多段except
捕捉多异常会显得特别麻烦,这个时候我们可以使用(异常类1,异常类2)
来捕捉多异常,但是需要注意的是,对比多段except
捕捉多异常来说,这种方式的处理逻辑会显得较为复杂(因为只有一段处理逻辑),如下:
# ==== 元组捕捉多异常 ==== while 1: li = [1,2,3,4] dic = {"name":"Yunya","age":18} li_index = input("请输入索引:") dic_key = input("请输入键的名称:") if li_index.isdigit(): li_index = int(li_index) try: print(li[li_index]) print(dic[dic_key]) except (IndexError,KeyError) as e: # 使用()的方式可以同时捕捉很多异常。 print("出错啦!") # ==== 执行结果 ==== """ 请输入索引:10 请输入键的名称:gender 出错啦! 请输入索引:2 请输入键的名称:gender 3 出错啦! """
可以看到,不管是那种错误都只有一种应对策略,如果我们想要多种应对策略就只能写if
判断来判断异常类型再做处理。所以就会显得很麻烦,如下:
# ==== 元组捕捉多异常 ==== while 1: li = [1,2,3,4] dic = {"name":"Yunya","age":18} li_index = input("请输入索引:") dic_key = input("请输入键的名称:") if li_index.isdigit(): li_index = int(li_index) try: print(li[li_index]) print(dic[dic_key]) except (IndexError,KeyError) as e: # 判断异常类型再做出相应的对应策略 if isinstance(e,IndexError): print("索引出错啦!") elif isinstance(e,KeyError): print("键出错啦!") # ==== 执行结果 ==== """ 请输入索引:10 请输入键的名称:gender 索引出错啦! 请输入索引:2 请输入键的名称:gender 3 键出错啦! """
万能异常Exception
我们可以捕捉Exception
类引发的异常,它是所有异常类的基类。(Exception
类的父类则是BaseException
类,而BaseException
的父类则是object类)
# ==== 万能异常Exception ==== while 1: li = [1,2,3,4] dic = {"name":"Yunya","age":18} li_index = input("请输入索引:") dic_key = input("请输入键的名称:") if li_index.isdigit(): li_index = int(li_index) try: print(li[li_index]) print(dic[dic_key]) except Exception as e: #使用 Exception来捕捉所有异常。 # 判断异常类型再做出相应的对应策略 if isinstance(e,IndexError): print("索引出错啦!") elif isinstance(e,KeyError): print("键出错啦!") # ==== 执行结果 ==== """ 请输入索引:10 请输入键的名称:gender 索引出错啦! 请输入索引:2 请输入键的名称:gender 3 键出错啦! """
try except else联用
这种玩法比较少,else
代表没有异常发生的情况下执行的代码,执行顺序如下:
try
中检测的代码块 —> 如果有异常 —> 终止try
中的代码块继续执行 —> 执行except
代码块 —> 执行正常逻辑代码 —> 程序结束
try
中检测的代码块 —> 如果没有异常 —> 执行完毕try
中的代码块 —> 执行else
代码块 —> 执行正常逻辑代码 —> 程序结束
# ==== try except else联用 有异常的情况==== li = [1,2,3] try: print("开始执行我try了...") print(li[10]) # 出错点... print("继续执行我try...") except IndexError as e: print("有异常执行我except...") else: print("没有异常执行我else...") print("正常逻辑代码...") # ==== 执行结果 ==== """ 开始执行我try了... 有异常执行我except... 正常逻辑代码... """
# ==== try except else联用 无异常的情况==== li = [1,2,3] try: print("开始执行我try了...") print(li[2]) print("继续执行我try...") except IndexError as e: print("有异常执行我except...") else: print("没有异常执行我else...") print("正常逻辑代码...") # ==== 执行结果 ==== """ 开始执行我try了... 3 继续执行我try... 没有异常执行我else... 正常逻辑代码... """
try except finally联用
finally
代表不论抛异常与否都会执行,因此常被用作关闭系统资源的操作,关于try
,except
,else
,finally
他们的优先级如下:
有异常的情况下:
try
代码块终止
try
代码块继续执行
except
代码块
finally
代码块正常逻辑
无异常的情况下:
try
代码块
else
代码块
finally
代码块正常逻辑
# ==== try except else finally 执行流程 有异常的情况 ==== li = [1,2,3] try: print("开始执行我try了...") print(li[10]) # 出错点... print("继续执行我try...") except IndexError as e: print("有异常执行我except...") else: print("没有异常执行我else...") finally: print("不管有没有异常都执行我finally...") print("正常逻辑代码...") # ==== 执行结果 ==== """ 开始执行我try了... 有异常执行我except... 不管有没有异常都执行我finally... 正常逻辑代码... """
# ==== try except else finally 执行流程 无异常的情况 ==== li = [1,2,3] try: print("开始执行我try了...") print(li[2]) print("继续执行我try...") except IndexError as e: print("有异常执行我except...") else: print("没有异常执行我else...") finally: print("不管有没有异常都执行我finally...") print("正常逻辑代码...") # ==== 执行结果 ==== """ 开始执行我try了... 3 继续执行我try... 没有异常执行我else... 不管有没有异常都执行我finally... 正常逻辑代码... """
自定义异常
raise主动抛出异常
在某些时候我们可能需要主动的去阻止程序的运行,主动的抛出一个异常。可以使用raise
来进行操作。这个是一种非常常用的手段。
# ==== raise使用方法 ==== print("----1----") print("----2----") print("----3----") raise Exception("我也不知道是什么类型的异常...") print("----4----") print("----5----") print("----6----") # ==== 执行结果 ==== """ ----1---- ----2---- ----3---- Traceback (most recent call last): File "C:/Users/Administrator/PycharmProjects/learn/元类编程.py", line 6, in <module> raise Exception("我也不知道是什么类型的异常...") Exception: 我也不知道是什么类型的异常... Process finished with exit code 1 """
自定义异常类
前面已经说过一切皆对象,异常也来自一个对象。因此我们也可以自己来定制一个对象。注意,自定义异常类必须继承BaseException
类。
# ==== 自定义异常类 ==== class MyError(BaseException): pass raise MyError("我的异常") # ==== 执行结果 ==== """ Traceback (most recent call last): File "C:/Users/Administrator/PycharmProjects/learn/元类编程.py", line 6, in <module> raise MyError("我的异常") __main__.MyError: 我的异常 """
扩展:断言assert
断言是一个十分装逼的使用,假设多个函数进行计算,我们已经有了预期的结果只是在做一个算法的设计。如果函数的最后的结果不是我们本来预期的结果那么宁愿让他停止运行也不要让错误继续扩大,在这种情况下就可以使用断言操作,使用断言会抛出一个AssertionError
类的异常。
# ==== 断言assert ==== def calculate(): """假设在做非常复杂的运算""" return 3+2*5 res = calculate() assert res == 25 # AssertionError print("算法测试通过!你真的太厉害了") # ==== 执行结果 ==== """ Traceback (most recent call last): File "C:/Users/Administrator/PycharmProjects/learn/元类编程.py", line 8, in <module> assert res == 25 AssertionError """