错误和异常
通常错误可被分为两种:语法错误 和 异常 。
Python 中有许多 内置异常 ,比如 NameError
、 TypeError
等。
异常的处理
使用 try 和 except 语句来处理异常。
while True:
try:
x = int(input('请输入一个数字:'))
break
except ValueError:
print('您输入的不是数字,请再次尝试输入!')
try 语句可以有多个 except 语句,分别处理不同的异常。也可以在 except 语句中使用元组来一同应对多个异常:
except (RuntimeError, TypeError, NameError):
pass
如果发生的异常是 except 子句中的异常类的子类,则该 except 子句会被触发:
class B(Exception):
pass
class C(B):
pass
for cls in [B, C]:
try:
raise cls()
except B:
print("B") # 只会触发该句
except C:
print("C")
except 子句可以在异常类后面指定一个变量,该变量会绑定到其异常实例上:
except OSError as err:
print("OS error: {0}".format(err))
异常对象的参数存储在 args
属性中。方便起见,Exception
类的 __str__()
方法会直接输出 args
属性的内容:
try:
raise Exception('spam', 'eggs')
except Exception as inst:
print(inst.args) # => ('spam', 'eggs')
print(inst) # => ('spam', 'eggs')
BaseException
是所有异常的共同基类。Exception
是它的一个子类,也是所有非致命异常的基类。非 Exception
子类的异常通常不被处理(比如 sys.exit()
引发的 SystemExit
异常),因为他们被用来指示程序应该终止。
try 语句还有一个可选的 else 子句,它只会在没有异常发生时执行:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except OSError:
print('cannot open', arg)
else:
# ...
f.close()
触发异常
使用 raise 语句来触发异常,语句的参数必须是一个异常实例或异常类(派生自 BaseException
的类):
raise NameError('HiThere')
raise ValueError # 会自动实例化,等同于 `raise ValueError()`
异常链
如果 except 子句有未处理的异常,则该异常会附加在原异常之后,一同在错误信息中显示:
try:
raise NameError('HiThere')
except NameError:
raise ValueError
# 会先输出 NameError 的错误信息,再输出 ValueError 的错误信息
为了表明异常是由另一个异常引发的,可以加上 from 语句:
try:
raise NameError('HiThere')
except NameError as e:
raise ValueError from e
from 语句的参数必须是一个异常实例或 None
,当为 None
时,会禁用自动异常链:
try:
raise NameError('HiThere')
except NameError:
raise ValueError from None
# 只会输出 ValueError 的错误信息
用户自定义异常
可以通过派生一个异常类来定义自己的异常:
class Error(Exception):
pass
定义清理操作
try 语句有个可选的 finally 语句,用于定义在任何情况下都会执行的清理操作:
try:
raise KeyboardInterrupt
finally:
print('Goodbye')
# 先打印 "GoodBye" ,再抛出异常
finally 遵循这样的执行规则:
- 如果在 try/except/else 子句中存在异常未处理,会先执行 finally 语句,再抛出异常。
- 如果 finally 子句中存在 break/continue/return 语句,则不会抛出异常。
- 如果 try 子句中存在 break/continue/return 语句,会先执行 finally 子句,再执行这些语句。
- 如果 finally 子句中存在 return 语句,则会直接返回该返回值,而不是 try 子句中的返回值。
预定义的清理操作
某些对象定义了标准清理操作,无论使用该对象的操作是否成功,都会执行清理操作。同时需要搭配 with 语句使用:
# 在语句执行完毕后,即便发生了异常,文件也会被正确关闭
with open('my_file.txt') as f:
for line in f:
print(line, end='')
引发和处理多个异常
可以用 ExceptionGroup
打包多个异常一块儿抛出:
try:
excs = [OSError('error 1'), SystemError('error 2')]
raise ExceptionGroup('there were problems', excs)
except Exception as e:
print(type(e)) # => <class 'ExceptionGroup'>
ExceptionGroup
支持嵌套:
raise ExceptionGroup(
"group1",
[
OSError(1),
SystemError(2),
ExceptionGroup(
"group2",
[
OSError(3),
RecursionError(4)
]
)
]
)
使用 except*
可以直接从异常组里提取异常:
try:
#...
except* OSError as e:
print("There were OSErrors")
except* SystemError as e:
print("There were SystemErrors")
注释异常
可以调用异常对象的 add_note()
方法来添加注释,它会在报错时与原报错信息一块显示。
try:
raise NameError('HiThere')
except NameError as e:
e.add_note('This is a note.')
raise