Python教程学习 (5)

参考:

8. 错误和异常 — Python 3.12.0 文档

语法错误

即SyntaxError

异常

异常有不同的类型,而类型名称会作为错误信息的一部分中打印出来

异常的处理

try...except...语句,和c/c++中的try catch差不多。

except 子句 可以用带圆括号的元组来指定多个异常。例如:

... except (RuntimeError, TypeError, NameError):
...     pass

如果发生的异常与 except 子句中的类是同一个类或是它的基类时,则该类与该异常相兼容

class B(Exception):
    pass

class C(B):
    pass

class D(C):
    pass

for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")

上面的例子中,会打印B, C, D。如果把 except B 放在最前,则会打印B,B,B。因为B是C/D的基类,因此满足前面的条件“except语句中的类是发生的异常的类的基类”。注意:前面手册中的中文翻译描述的不太清楚,应该按照我这里写的内容去理解。

try ... except 语句具有可选的 else 子句,该子句如果存在,它必须放在所有 except 子句 之后。 它适用于 try 子句 没有引发异常但又必须要执行的代码。

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except OSError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

上面的代码打开文件的时候,检查是否文件存在。如果存在,才执行else中的读取文件的行数功能。

触发异常

raise 语句支持强制触发指定的异常。

如果只想判断是否触发了异常,但并不打算处理该异常,则可以使用更简单的 raise 语句重新触发异常。

>>> try:
...   raise NameError('HiThere')
... except NameError:
...   print('An exception flew by!')
...
An exception flew by!
>>>
>>> try:
...   raise NameError('HiThere')
... except NameError:
...   print('An exception flew by!')
...   raise
...
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: HiThere

比较上面例子中有raise和没有raise的区别。注意到raise依然抛出的是“raise NameError('HiThere')”这句中的异常,因此是“重新触发异常”。

异常链

就是在except子句中再次raise一个异常。在异常打印中,会将这条链上的每一个raise的异常都打印出来。

用户自定义异常

用户可以自己定义自己的异常类,但都应该直接或间接地从Exception 类派生。

大多数异常命名都以 “Error” 结尾,类似标准异常的命名。

定义清理操作

如果存在 finally 子句,则 finally 子句是 try 语句结束前执行的最后一项任务。不论 try 语句是否触发异常,都会执行 finally 子句。以下内容介绍了几种比较复杂的触发异常情景:

  • 如果执行 try 子句期间触发了某个异常,则某个 except 子句应处理该异常。如果该异常没有 except 子句处理,在 finally 子句执行后会被重新触发。(理解:如果异常没有except子句处理,异常会被系统处理,且 finally 子句的执行位于系统处理之前
  • exceptelse 子句执行期间也会触发异常。 同样,该异常会在 finally 子句执行之后被重新触发。(TODO:
  • 如果 finally 子句中包含 breakcontinuereturn 等语句,异常将不会被重新引发。
  • 如果执行 try 语句时遇到 breakcontinuereturn 语句,则 finally 子句在执行 breakcontinuereturn 语句之前执行。(理解:break/continue/return可以认为是try语句块的退出,因此会在退出之前执行finally子句
  • 如果 finally 子句中包含 return 语句,则返回值来自 finally 子句的某个 return 语句的返回值,而不是来自 try 子句的 return 语句的返回值。(理解::因为finally子句会先于try中的return执行,因此先执行的是finally子句中的return,也就直接return了,所以不会再执行try中的return了)
>>> def bool_return():
...   try:
...     return True
...   finally:
...     return False
...
>>> bool_return()
False

理解上面的规则后,教程中的“复杂的例子”不难理解。

>>> def divide(x, y):
...   try:
...     result = x / y
...   except ZeroDivisionError:
...     print("division by zero!")
...   else:
...     print("result is", result)
...   finally:
...     print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

在实际应用程序中,finally 子句对于释放外部资源(例如文件或者网络连接)非常有用,无论是否成功使用资源。

预定义的清理操作

with语句对于文件操作的自动清理

引发和处理多个不相关的异常

内置的 ExceptionGroup 打包了一个异常实例的列表,这样它们就可以一起被引发。

>>> def f():
...   excs = [OSError('error 1'), SystemError('error 2')]
...   raise ExceptionGroup('there were problems', excs)
...
>>> try:
...   f()
... except Exception as e:
...   print(f'caught {type(e)}: e')
...   raise
...
caught <class 'ExceptionGroup'>: e
  + Exception Group Traceback (most recent call last):
  |   File "<stdin>", line 2, in <module>
  |   File "<stdin>", line 3, in f
  | ExceptionGroup: there were problems (2 sub-exceptions)
  +-+---------------- 1 ----------------
    | OSError: error 1
    +---------------- 2 ----------------
    | SystemError: error 2
    +------------------------------------

上面的例子中,在excs这个序列中定义了2个异常,因此raise的时候使用了ExceptionGroup打包了这个异常列表。在try...except...语句中,如果except子句中没有raise,则只会打印“caught <class 'ExceptionGroup'>: e”。

通过使用 except* 代替 except ,我们可以有选择地只处理组中符合某种类型的异常。

def f():
    raise ExceptionGroup(
        "group1",
        [
            OSError(1),
            SystemError(2),
            ExceptionGroup(
                "group2",
                [
                    OSError(3),
                    RecursionError(4)
                ]
            )
        ]
    )


try:
    f()
except* OSError as e:
    print("There were OSErrors")
except* SystemError as e:
    print("There were SystemErrors")

# Printings as below
There were OSErrors
There were SystemErrors
  + Exception Group Traceback (most recent call last):
  |   File "<stdin>", line 1, in <module>
  |   File "C:\D\03_programTry\pyExec\test_try_except_star.py", line 19, in <module>
  |     f()
  |   File "C:\D\03_programTry\pyExec\test_try_except_star.py", line 2, in f
  |     raise ExceptionGroup(
  | ExceptionGroup: group1 (1 sub-exception)
  +-+---------------- 1 ----------------
    | ExceptionGroup: group2 (1 sub-exception)
    +-+---------------- 1 ----------------
      | RecursionError: 4
      +------------------------------------

上面的例子中,如果没有“RecursionError(4)”,则只会打印"There were OSErrors"和"There were SystemErrors"。也就是说,使用except *之后,系统对异常的打印只会发生在没有出现在自定义的except捕获对象中

用注释细化异常情况

对于捕获的异常,定义 了方法add_note(),为这个异常增加更多的信息。

>>> def f():
...   raise OSError('operation failed')
...
>>> excs = []
>>> for i in range(3):
...   try:
...     f()
...   except Exception as e:
...     e.add_note(f'Happened in Iteration {i+1}')
...     excs.append(e)
...
  + Exception Group Traceback (most recent call last):
  |   File "<stdin>", line 1, in <module>
  | ExceptionGroup: We have some problems (3 sub-exceptions)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "<stdin>", line 3, in <module>
    |   File "<stdin>", line 2, in f
    | OSError: operation failed
    | Happened in Iteration 1
    +---------------- 2 ----------------
    | Traceback (most recent call last):
    |   File "<stdin>", line 3, in <module>
    |   File "<stdin>", line 2, in f
    | OSError: operation failed
    | Happened in Iteration 2
    +---------------- 3 ----------------
    | Traceback (most recent call last):
    |   File "<stdin>", line 3, in <module>
    |   File "<stdin>", line 2, in f
    | OSError: operation failed
    | Happened in Iteration 3
    +------------------------------------

上面的例子中,如果没有“raise ExceptionGroup('We have some problems', excs)”,则循环中捕获的3个异常都会被添加到序列excs中,并且每次都添加了“Happened in Iteration X”的信息。此时不会有任何打印,因为所有发生的异常都被自定义的try...except...捕获了。一旦主动raise以序列excs为基础创建的ExceptionGroup时,系统就会处理这个异常组,从而给出系统默认的打印。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值