跳到主要内容

错误和异常

通常错误可被分为两种:语法错误异常

Python 中有许多 内置异常 ,比如 NameErrorTypeError 等。

异常的处理

使用 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

参考

错误和异常 - python.org