错误和异常
通常错误可被分为两种:语法错误 和 异常 。
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