Python 异常处理机制
-
- Python异常与异常处理机制
- [针对 Traceback 的解读](#针对 Traceback 的解读)
- try-except-else-finally
-
- except语句
-
- except语句的机制
- [在 except 语句中引用当前被处理的 Python 异常](#在 except 语句中引用当前被处理的 Python 异常)
- finally语句
- [raise 语句](#raise 语句)
-
- [raise 语句以及异常对象](#raise 语句以及异常对象)
- [raise...from 语句](#raise…from 语句)
- [Python 异常的基类 BaseException](#Python 异常的基类 BaseException)
Python异常与异常处理机制
Python使用称为异常的特殊对象来管理程序执行期间发生的错误(Python中一切皆对象,包括异常也是对象)。每当发生程序错误,都会创建一个异常对象。如果编写了处理该异常的代码,程序将继续运行;但是如果未编写代码对异常进行处理,程序将停止并显示traceback,其中包含有关异常的报告
异常处理是编程语言或计算机硬件里的一种机制,用于处理软件或信息系统中出现的异常状况,即超出程序正常执行流程的某些特殊条件。异常处理机制是使用 try-except 代码块处理异常,try-except 代码块让Python执行指定的操作,同时告诉Python发生异常时怎么办。使用try-except 代码块时,即便出现异常,程序也将继续运行,同时显示你编写的友好的错误消息,而不是令用户迷惑的traceback
Python提供了两个非常重要的功能来处理程序在运行中出现的异常和错误。经常使用的是try...except语句,拓展一下就是try-except-else-finally,另一个是断言,即assert
针对 Traceback 的解读
Traceback 是 Python 错误信息的报告,当你的程序导致异常时,Python 将打印 Traceback 以帮助你知道哪里出错了。虽然 Python 的 Traceback 提示信息看着挺复杂,但是里面存在丰富的信息,可以帮助你诊断和修复代码中引发异常的原因,以及定位到具体哪个文件的哪行代码出现的错误,这样有助于我们编写对应的异常处理代码来捕获异常,并给出合适的错误提示信息。所以说学会看懂 Traceback 信息是非常重要的
下面给出一个案例进行分析,这是一种导致Python引发异常的简单错误:除0
python
# calculate.py文件的内容
def divide(a, b): # 进行除法运算的函数
return a / b
# main.py文件的内容
from calculate import divide
divide(5, 0)
# 抛出异常:
"""
Traceback (most recent call last):
File "C:\编程\Python代码\test\main.py", line 3, in <module>
divide(5, 0)
File "C:\编程\Python代码\test\calculate.py", line 2, in divide
return a / b
ZeroDivisionError: division by zero
"""
- 错误输出的最后一行一般会告诉你引发了什么类型的异常,以及关于该异常的一些相关信息,所以看错误信息的最后一行就能获知到错误的原因
- 在上述 traceback 中,最后一行指出的错误 ZeroDivisionError 就是一个异常对象,并且给出了具体的描述 division by zero,即表示因为除0,导致程序抛出该异常
- 错误输出的前面几行的阅读顺序应该是由下而上的,因为越往上,离抛出异常的实际位置越远,越难定位到抛出异常的代码
- 这一部分要每连续两行为一组进行阅读。其中每组的第1行会告诉你是在哪个文件的、哪个函数的、哪一行出错了,也就是会更直接的告诉你错误发生的位置;每组的第2行会把报错的那一行代码显示出来
- 比如在程序里A函数调用了B函数,然后B函数调用了C函数,而C函数报错了,那么 traceback 中的前几行从下至上的顺序来看,会先显示C函数的信息,再显示B函数的信息,再显示A函数的信息,而且每一个函数的信息都是按两行来组织的,其中第1行展示位置,第2行展示代码
- 在上述 traceback 中,前面几行中的最后一行定位的位置为位于 calculate.py 文件中的第2行,并且位于 divide 函数代码块中,具体抛出异常的代码为"return a / b"
- 然后我们看上一行,定位的位置为位于 main.py 文件中的第3行,并且位于引入模块的代码中,具体抛出异常的代码为"divide(5, 0)"
- 最上面第一行的内容是固定不变的,始终Traceback (most recent call last)",可以忽略
try-except-else-finally
- try:正常情况下,程序计划执行的语句
- except:程序异常时执行的语句
- else:程序无异常即try段代码正常执行后会执行该语句
- 当try语句的相关代码中的return,continue或break语句被执行时,else语句将被忽略
- finally:不管有没有异常,都会执行的语句
- 具体来说,当try,except或else语句的相关代码中存在某些跳转语句时,比如break,continue和return,与finally语句相关的代码将在这些跳转语句执行之前被执行
- 在处理 Python 异常的过程中,一些代码需要始终被执行,无论是否有 Python 异常被抛出,或 Python 异常是否被处理。使用finally语句可以达成上述目标,该语句之后的代码通常与清理工作有关,比如,关闭打开的文件
运行逻辑1:首先运行try中的代码块,当出现异常时,终止try中代码块的执行,立即执行except中的代码块,最后执行finally中的代码块
python
try:
print('输出:我是try1')
a = 5 / 0
print('输出:我是try2')
except :
print('输出:我是except')
else :
print('输出:我是else')
finally :
print('输出:我是finally')
# 输出结果:
"""
输出:我是try1
输出:我是except
输出:我是finally
"""
运行逻辑2:首先运行try中的代码块,当执行完毕后,代码始终没有抛出异常,继续执行else中的代码块,最后执行finally中的代码块
python
try:
print('输出:我是try1')
a = 5 / 1
print('输出:我是try2')
except :
print('输出:我是except')
else :
print('输出:我是else')
finally :
print('输出:我是finally')
# 输出结果:
"""
输出:我是try1
输出:我是try2
输出:我是else
输出:我是finally
"""
运行逻辑3:函数中,如果finally语句的相关代码中包含了return语句,那么该return语句所返回的值(包括空值None),将取代try,except或else语句相关代码中的返回值
因为finally中的代码块在设定上必须执行:
- 在try中的代码块return之前,会执行finally中的语句,try中的return被忽略了,最终返回的值是finally中return值
- try中的代码块没有return语句,执行完毕后,会继续执行else中的语句,在else中的代码块return之前,执行finally中的语句,else中的return被忽略了,最终返回的值是finally中return值
- try中的代码块中抛出异常,被except捕获,在except中的代码块return之前,执行finally中的语句,except中的return被忽略了,最终返回的值是finally中return值
python
def test():
try:
print('输出:我是try1')
a = 5 / 0
print('输出:我是try2')
return 1
except :
print('输出:我是except1')
return 2
print('输出:我是except2')
else :
print('输出:我是else')
return 3
finally :
print('输出:finally')
return 4
num = test()
print(num)
# 输出结果:
"""
输出:我是try1
输出:我是except1
输出:finally
4
"""
def test():
try:
print('输出:我是try1')
a = 5 / 1
print('输出:我是try2')
return 1
except :
print('输出:我是except')
return 2
else :
print('输出:我是else')
return 3
finally :
print('输出:finally')
return 4
num = test()
print(num)
# 输出结果:
"""
输出:我是try1
输出:我是try2
输出:finally
4
"""
def test():
try:
print('输出:我是try1')
a = 5 / 1
print('输出:我是try2')
# return 1
except :
print('输出:我是except')
return 2
else :
print('输出:我是else1')
return 3
print('输出:我是else2')
finally :
print('输出:finally')
return 4
num = test()
print(num)
# 输出结果:
"""
输出:我是try1
输出:我是try2
输出:我是else1
输出:finally
4
"""
运行逻辑4:函数中,finally中不存在return,并且try中代码块没有抛出异常,那么按照 运行逻辑2,函数返回的是try或者else代码中先出现的那个return值
因为代码中程序正常运行,不一定要执行else中的代码块,如果try中return直接结束函数执行了,那么就不会执行else中的代码块
python
def test():
try:
print('输出:我是try1')
a = 5 / 1
print('输出:我是try2')
return 1
except :
print('输出:我是except')
return 2
else :
print('输出:我是else')
return 3
finally : # finally中不存在return语句
print('输出:finally')
# return 4
num = test()
print(num)
# 输出结果:
"""
输出:我是try1
输出:我是try2
输出:finally
1
"""
def test():
try:
print('输出:我是try1')
a = 5 / 1
print('输出:我是try2')
# return 1
except :
print('输出:我是except')
return 2
else :
print('输出:我是else')
return 3
finally : # finally中不存在return语句
print('输出:finally')
# return 4
num = test()
print(num)
# 输出结果:
"""
输出:我是try1
输出:我是try2
输出:我是else
输出:finally
3
"""
运行逻辑5:函数中,finally中不存在return,并且try中代码块产生异常,那么按照 运行逻辑1,函数返回的是except代码中的那个return值
因为代码中程序抛出异常,必须执行except中的代码块
python
def test():
try:
print('输出:我是try1')
a = 5 / 0
print('输出:我是try2')
return 1
except :
print('输出:我是except')
return 2
else :
print('输出:我是else')
return 3
finally : # finally中不存在return语句
print('输出:finally')
# return 4
num = test()
print(num)
# 输出结果:
"""
输出:我是try1
输出:我是except
输出:finally
2
"""
except语句
except语句的机制
每一个try语句,都必须至少有一个except语句,除非存在 finally 语句
- 一般一个try语句后面都需要存在至少一个except语句,但是使用 finally 语句后 except 语句将成为可选的,try语句之后可以没有任何except语句
python
try:
print("No Error")
# 抛出异常:SyntaxError: unexpected EOF while parsing
try:
5/0
# 抛出异常:SyntaxError: unexpected EOF while parsing
try:
print("No Error")
finally:
print("Something must be done")
# 输出结果:
"""
No Error
Something must be done
"""
try:
5/0
finally:
print("Something must be done")
# 输出结果:
"""
Something must be done
Traceback (most recent call last):
File "C:\编程\Python代码\test\test.py", line 3, in <module>
5/0
ZeroDivisionError: division by zero
"""
使用 else 语句的前提是至少拥有一个 except 语句
- 如果要使用else语句来处理没有 Python 异常被引发的情况,那么在try语句之后,必须至少存在一个except语句
python
try:
print("No Error")
else:
print("must have except")
finally:
print("Something must be done")
# 抛出异常:SyntaxError: invalid syntax
try:
print("No Error")
except:
print("waiting for error")
else:
print("must have except")
finally:
print("Something must be done")
# 输出结果:
"""
No Error
must have except
Something must be done
"""
一个try...except语句中可以拥有多个except语句,都是最多只有一个 except 语句能与被抛出 Python 异常匹配
- 如果try...except语句拥有多个except语句,那么与 Python 的 if 语句类似,他们会按照先后顺序进行匹配,当某一个except语句与被抛出的 Python 异常匹配时,其余的except语句将被忽略
python
try:
5/0
except IndexError as e: # 无法捕获除0异常
print("IndexError")
except ZeroDivisionError as e: # 可以捕获除0异常
print("ZeroDivisionError1")
except ZeroDivisionError as e: # 可以捕获除0异常
print("ZeroDivisionError2")
except BaseException as e: # 可以捕获除0异常
print("BaseException")
# 输出结果:ZeroDivisionError1
try:
5/0
except IndexError as e: # 无法捕获除0异常
print("IndexError")
except BaseException as e: # 可以捕获除0异常
print("BaseException")
except ZeroDivisionError as e: # 可以捕获除0异常
print("ZeroDivisionError1")
except ZeroDivisionError as e: # 可以捕获除0异常
print("ZeroDivisionError2")
# 输出结果:BaseException
except可以处理一个专门的异常,也可以处理包含在元组中的一组异常
- 可以将多个异常类型放在一个括号中,通过逗号分隔。当try块引发其中任何一个异常时,程序都将跳转到该except块,处理这个异常
python
try:
5 / 0
except (IndexError, ZeroDivisionError) as e: # 可以捕获除0异常
print("IndexError or ZeroDivisionError")
except BaseException as e: # 可以捕获除0异常
print("ZeroDivisionError1")
# 输出结果:IndexError or ZeroDivisionError
如果except后没有指定异常类型,则默认捕获所有的异常
- 此时捕获异常范围为BaseException(后面会提到)
- 这样的没有指定异常类型的 except 语句要求必须是最后一个 except 语句,否则程序报错
- 原因很简单,如果它位于其他except语句之前,那么一些except语句将失去被执行的可能
python
try:
5 / 0
except: # 可以捕获所有异常
print("捕获到异常")
# 输出结果:捕获到异常
与所有 except 语句均不匹配的 Python 异常将被重新抛出
- 如果一个 Python 异常被引发,并且与所有的except语句均不匹配,那么该异常将作为未处理的 Python 异常被重新抛出,这可能导致整个程序因此结束执行(当然,finally中的代码块依旧会被执行)
python
try:
5/0
except IndexError as e: # 无法捕获除0异常
print("IndexError")
except KeyError as e: # 无法捕获除0异常
print("KeyError")
except ValueError as e: # 无法捕获除0异常
print("ValueError")
finally:
print('输出:finally')
# 抛出异常:
"""
输出:finally
Traceback (most recent call last):
File "C:\编程\Python代码\test\main.py", line 2, in <module>
5/0
ZeroDivisionError: division by zero
"""
在 except 语句中引用当前被处理的 Python 异常
在except语句中,使用as关键字可以指定一个与被处理异常绑定(指向异常对象)的标识符(可将其简单的视为 Python 变量),即可通过该标识符在except语句相关的代码中访问被处理的 Python 异常
此外,在 3.11 或更高版本中,通过sys模块的exception函数,同样可在except语句相关的代码中访问当前被处理的 Python 异常
- 异常对象的常用属性
- args:包含有关异常的错误编号和异常的描述的元组
- strerror:异常的描述
- errno:与异常的错误编号
python
try:
open("no_exit_file.txt", "r")
except FileNotFoundError as e: # 可以捕获除0异常
print("异常对象:", e)
print("异常类型:", type(e))
# 异常对象的常用属性
print("异常信息: ", e.args) # 该属性返回异常的错误编号和描述
print("异常描述: ", e.strerror) # 该属性返回异常的描述
print("错误号: ", e.errno) # 该属性返回异常的错误编号
# 输出结果:
"""
异常对象: [Errno 2] No such file or directory: 'no_exit_file.txt'
异常类型: <class 'FileNotFoundError'>
异常信息: (2, 'No such file or directory')
异常描述: No such file or directory
错误号: 2
"""
except 语句会删除使用 as 关键字与 Python 异常绑定的标识符
- 如果在某个except语句中使用了as关键字,那么as关键字指定的标识符,将在该except语句的相关代码执行完毕时被删除。这意味着标识符仅在except语句中保持其正确性,他不应该与同一命名空间的其他标识符重复,以避免一些不必要的错误
python
try:
open("no_exit_file.txt", "r")
except FileNotFoundError as e: # 可以捕获除0异常
print("异常对象:", e)
print("异常类型:", type(e))
print(e)
# 输出结果:
"""
异常对象: [Errno 2] No such file or directory: 'no_exit_file.txt'
异常类型: <class 'FileNotFoundError'>
Traceback (most recent call last):
File "C:\编程\Python代码\test\test.py", line 9, in <module>
print(e)
NameError: name 'e' is not defined
"""
finally语句
finally语句执行后才能抛出未被处理的异常
当try,except或else语句的相关代码引发不能被处理的 Python 异常时,这些异常不会被立即抛出,他们需要等待finally语句的相关代码的执行
python
try:
5/0
except IndexError as e: # 无法捕获除0异常
print("IndexError")
except KeyError as e: # 无法捕获除0异常
print("KeyError")
except ValueError as e: # 无法捕获除0异常
print("ValueError")
finally:
print('输出:finally')
# 抛出异常:
"""
输出:finally
Traceback (most recent call last):
File "C:\编程\Python代码\test\main.py", line 2, in <module>
5/0
ZeroDivisionError: division by zero
"""
try:
5 / 0
except ZeroDivisionError as e:
print("ZeroDivisionError异常被捕获")
raise e
finally:
print('输出:finally')
# 输出结果:
"""
ZeroDivisionError异常被捕获
输出:finally
Traceback (most recent call last):
File "C:\编程\Python代码\test\main.py", line 6, in <module>
raise e
File "C:\编程\Python代码\test\main.py", line 2, in <module>
5 / 0
ZeroDivisionError: division by zero
"""
try:
5 / 1
except ZeroDivisionError as e:
print("ZeroDivisionError异常被捕获")
else:
print('输出:else')
raise ZeroDivisionError("重新抛出的ZeroDivisionError")
finally:
print('输出:finally')
# 输出结果:
"""
输出:else
输出:finally
Traceback (most recent call last):
File "C:\编程\Python代码\test\main.py", line 9, in <module>
raise ZeroDivisionError("重新抛出的ZeroDivisionError")
ZeroDivisionError: 重新抛出的ZeroDivisionError
"""
finally中执行return会导致异常丢失
如果finally语句的相关代码中包含了跳转语句,比如break,continue或return,那么这些跳转语句的执行,将导致未被except处理的 Python 异常不再被重新抛出,即便这些异常是通过raise语句主动抛出的
3.8 版本之前,在 Python 的finally语句的相关代码中,不能使用continue语句
finally中出现return语句,如果try中抛出的异常没有被捕获到,按理说当finally执行完毕后,应该被再次抛出,但finally里执行了return,导致异常被丢失,所以实际应用中,不推荐在finally中使用return返回
python
def test():
try:
5/0
print("函数中的后续代码被执行")
except IndexError as e: # 无法捕获除0异常
print("IndexError")
except KeyError as e: # 无法捕获除0异常
print("KeyError")
except ValueError as e: # 无法捕获除0异常
print("ValueError")
finally:
print('输出:finally')
return 1
num = test()
print(num)
print("后续代码被执行")
# 输出结果:
"""
输出:finally
1
后续代码被执行
"""
raise 语句
raise 语句以及异常对象
使用 Python 提供的raise语句,开发人员可以主动抛出(引发)一个 Python 异常
python
raise 语句基本的语法形式为 raise <exception>
- exception
- 被抛出的 Python 异常对象,或 Python 异常类型如果仅给出异常类型,那么将根据该类型隐式创建其对应的实例,比如,raise ZeroDivisionError的效果等同于raise ZeroDivisionError()
python
raise ZeroDivisionError
# 抛出异常:
"""
Traceback (most recent call last):
File "C:\编程\Python代码\test\main.py", line 1, in <module>
raise ZeroDivisionError
ZeroDivisionError
"""
# 前后对比,发现效果一致
raise ZeroDivisionError()
# 抛出异常:
"""
Traceback (most recent call last):
File "C:\编程\Python代码\test\main.py", line 1, in <module>
raise ZeroDivisionError()
ZeroDivisionError
"""
创建异常对象时可以指定其魔法方法__str__的返回值
python
# 判断ZeroDivisionError()返回的东西是否为ZeroDivisionError的实例对象
ins = ZeroDivisionError()
flag = isinstance(ins, ZeroDivisionError)
print(flag)
# 输出结果:True
ins = ZeroDivisionError()
dir(ins) # 可以获知,该实例对象具有魔法方法__str__
# 输出结果:
"""
['__cause__', '__class__', '__context__', '__delattr__', '__dict__', '__dir__',
'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__',
'__subclasshook__', '__suppress_context__', '__traceback__', 'args', 'with_traceback']
"""
# 由于调用print函数实际是执行实例对象的魔法方法__str__,打印结果为该方法返回值
# 所以可以推断出实例化异常对象时,传入的"This is a ZeroDivisionError"
# 实际就是魔法方法__str__的返回值
ins = ZeroDivisionError("This is a ZeroDivisionError")
print(ins)
# 输出结果:This is a ZeroDivisionError
ins = ZeroDivisionError("This is a ZeroDivisionError")
describe_string = ins.__str__() # 手动调用魔法方法__str__,打印该方法的返回值
print(describe_string)
# 输出结果:This is a ZeroDivisionError
data = 10
ins = ZeroDivisionError("This is a ZeroDivisionError, carry a data: %s" % data )
print(ins)
# 输出结果:This is a ZeroDivisionError, carry a data: 10
在except语句中,可以使用 raise 语句,将raise语句的exception部分留空,这会将当前被处理的 Python 异常重新抛出(引发)
但是以上做法的效果并不等同于调用exception函数的语句raise sys.exception(),或类似于raise err的语句(假设err为as关键字绑定的标识符),他们会展示不同的回溯(Traceback)信息
python
# calculate.py文件的内容
def divide(a, b): # 进行除法运算的函数
return a / b
# main.py文件的内容
from calculate import divide
divide(5, 0)
# 抛出异常:
"""
Traceback (most recent call last):
File "C:\编程\Python代码\test\main.py", line 3, in <module>
divide(5, 0)
File "C:\编程\Python代码\test\calculate.py", line 2, in divide
return a / b
ZeroDivisionError: division by zero
"""
# calculate.py文件的内容
def divide(a, b): # 进行除法运算的函数
return a / b
# main.py文件的内容
from calculate import divide
try:
divide(5, 0)
except ZeroDivisionError:
print("ZeroDivisionError异常被捕获")
raise # 使用raise重新抛出异常
# 抛出异常:
"""
ZeroDivisionError异常被捕获
Traceback (most recent call last):
File "C:\编程\Python代码\test\test.py", line 4, in <module>
divide(5, 0)
File "C:\编程\Python代码\test\calculate.py", line 2, in divide
return a / b
ZeroDivisionError: division by zero
"""
# calculate.py文件的内容
def divide(a, b): # 进行除法运算的函数
return a / b
# main.py文件的内容
from calculate import divide
try:
divide(5, 0)
except ZeroDivisionError as e:
print("ZeroDivisionError异常被捕获")
raise e
# 抛出异常:
"""
ZeroDivisionError异常被捕获
Traceback (most recent call last):
File "C:\编程\Python代码\test\test.py", line 8, in <module>
raise e
File "C:\编程\Python代码\test\test.py", line 4, in <module>
divide(5, 0)
File "C:\编程\Python代码\test\calculate.py", line 2, in divide
return a / b
ZeroDivisionError: division by zero
"""
抛出异常时,自动创建的异常对象的魔法方法__str__的返回值即为 traceback 最后一行针对该异常的描述信息
python
5 / 0
# 抛出异常:
"""
Traceback (most recent call last):
File "C:\编程\Python代码\test\main.py", line 1, in <module>
5 / 0
ZeroDivisionError: division by zero
"""
try:
5 / 0
except ZeroDivisionError as e:
# 判断 e 是否为ZeroDivisionError的实例对象
flag = isinstance(e, ZeroDivisionError)
print(flag)
# 手动调用魔法方法__str__,打印该方法的返回值,进行对比
describe_string = e.__str__()
print(e)
print(describe_string)
# 输出结果:
"""
True
division by zero
division by zero
"""
try:
5 / 0
except ZeroDivisionError as e:
print("自动抛出的异常被捕获")
raise e
# 输出结果:
"""
自动抛出的异常被捕获
Traceback (most recent call last):
File "C:\编程\Python代码\test\main.py", line 6, in <module>
raise e
File "C:\编程\Python代码\test\main.py", line 2, in <module>
5 / 0
ZeroDivisionError: division by zero
"""
自行创建一个异常对象,通过raise抛出,同时携带自定义的描述信息
python
try:
5 / 0
except ZeroDivisionError as e:
print("自动抛出的异常被捕获")
raise ZeroDivisionError("<division by zero>")
# 输出结果:
"""
自动抛出的异常被捕获
Traceback (most recent call last):
File "C:\编程\Python代码\test\main.py", line 6, in <module>
raise ZeroDivisionError("<division by zero>")
ZeroDivisionError: <division by zero>
"""
raise...from 语句
如果一个 Python 异常已经被某个except语句处理,而该except语句的相关代码引发了新的异常,如果新的 Python 异常是通过raise...from语句引发,那么可以为新的 Python 异常指定一个表示原因的异常,用于说明新的 Python 异常是由该异常导致的
raise...from语句可以在其他位置使用,如果位于except语句的相关代码中,那么表示原因的异常一般被指定为已被except处理的 Python 异常,此时回溯信息将优先展示表示原因的 Python 异常的信息
python
raise...from 语句基本的语法形式为 raise <newexception> from <causeexception>
-
newexception
- 被抛出的 Python 异常对象,或 Python 异常类型。如果仅给出异常类型,那么将根据该类型隐式创建其对应的实例,比如,raise RuntimeError from ValueError()的效果等同于raise RuntimeError() from ValueError()
-
causeexception
- 表示原因的 Python 异常对象,或 Python 异常类型。如果仅给出异常类型,那么将根据该类型隐式创建其对应的实例,比如,raise RuntimeError() from ValueError的效果等同于raise RuntimeError() from ValueError()
Python 异常的基类 BaseException
在 Python 中,所有异常(表示异常的类)都需要继承自BaseException或Exception,这包括 Python 的内置异常,以及由开发人员定义的异常(当然,只有少数 Python 异常直接继承自类BaseException,大部分 Python 异常均继承自类Exception或类Exception的派生类)
- Python 异常基类 BaseException 和 Exception 之间的区别
- Exception是BaseException类的派生类,他表示不是来自于系统的非正常情况,比如,表示除数为0的 Python 异常ZeroDivisionError
- 一般情况下,开发人员仅需要捕获从Exception类派生的各种 Python 异常,如果将捕获的范围扩大到BaseException,那么可能会导致一些意想不到的问题
- 如果except后没有指定异常类型,则默认捕获所有的异常,即此时捕获异常范围为BaseException, 这样的没有指定异常类型的 except 语句要求必须是最后一个 except 语句,否则程序报错,原因很简单,如果它位于其他except语句之前,那么一些except语句将失去被执行的可能
如果将捕获异常的范围扩大到BaseException,可能会导致sys.exit()无法退出 Python 解释器
python
import sys
try:
sys.exit() # 尝试退出程序
except BaseException as e:
print(f'捕获到了异常 {type(e)}')
print('sys.exit() 已经执行,但是程序没有成功退出')
# 输出结果:
"""
捕获到了异常 <class 'SystemExit'>
sys.exit() 已经执行,但是程序没有成功退出
"""