>>> from env_helper import info; info()
页面更新时间: 2024-01-13 09:59:54
运行环境:
Linux发行版本: Debian GNU/Linux 12 (bookworm)
操作系统内核: Linux-6.1.0-16-amd64-x86_64-with-glibc2.36
Python版本: 3.11.2
3.6. Python中的异常处理¶
到目前为止,在 Python 程序中遇到错误,或“异常”,意味着整个程序崩溃。 你不希望这发生在真实世界的程序中。 相反,你希望程序能检测错误,处理它们,然后继续运行。
3.6.1. 运行时的错误程序¶
例如,考虑下面的程序,它有一个“除数为零”的错误。
>>> def spam(divideBy):
>>> return 42 / divideBy
>>>
>>> print(spam(2))
>>> print(spam(12))
>>> print(spam(1))
21.0
3.5
42.0
我们已经定义了名为 spam
的函数,给了它一个变量,然后打印出该函数带各种参数的值。
看看参数是 0 会发生什么情况,下面是运行的输出:
>>> print(spam(0))
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Cell In [14], line 1
----> 1 print(spam(0))
Cell In [13], line 2, in spam(divideBy)
1 def spam(divideBy):
----> 2 return 42 / divideBy
ZeroDivisionError: division by zero
当试图用一个数除以零时,就会发生 ZeroDivisionError 。
根据错误信息中给出的行号, 我们知道 spam() 中的 return
语句导致了一个错误。
一旦出现这样的错误,程序就会中断。避免程序在有错误的情况下继续运行是一种保护机制,以防及时发出通过并解决问题。
3.6.2. 函数作为“黑盒”¶
通常,对于一个函数,你要知道的就是它的输入值(变量)和输出值。 你并非总是需要加重自己的负担,弄清楚函数的代码实际是怎样工作的。 如果以这种高层的方式来思考函数, 通常大家会说,你将该函数看成是一个黑盒。 这个思想是现代编程的基础。
很多模块其中的函数是由其他人编写的。 尽管你在好奇的时候也可以看一看源代码, 但要使用它们你并不需要知道它们是如何工作的。 另外 ,作为编程的建议实践,鼓励在编写函数时不使用全局变量, 通常也不必担心函数的代码会与程序的其他部分发生交叉影响。
3.6.3. except 语句¶
在Python语言中,异常由 try 和 except 语句来处理。
那些可能出错的语句被放在 try 子句中。
如果错误发生,程序执行就转到接下来的 except 子句开始处。
可以将前面除数为零的代码放在一个 try 子句中, 让 except
子句包含代码,来处理该错误发生时需要做的事。
>>> def spam(divideBy):
>>> try:
>>> return 42 / divideBy
>>> except ZeroDivisionError:
>>> print('Error: Invalid argument.')
>>>
>>> print(spam(2))
21.0
现在传递参数 0 给这个定义的函数:
>>> print(spam(0))
Error: Invalid argument.
None
如果在 try 子句中的代码导致一个错误, 程序执行就立即转到 except
子句的代码。
3.6.4. except 语句的执行顺序¶
请注意,在函数调用中的 try 语句块中,发生的所有错误都会被捕捉。
请考虑以下程序,它的做法不一样,将 spam() 调用放在语句块中:
>>> def spam(divideBy):
>>> return 42 / divideBy
>>>
>>> try:
>>> print(spam(12))
>>> print(spam(0))
>>> print(spam(1))
>>> except ZeroDivisionError:
>>> print('Error: Invalid argument.')
3.5
Error: Invalid argument.
在上面的代中 print(spam(1)) 永远不会被执行。 程序一旦执行跳到
except 子句的代码,会继续照常向下执行,不会回到 try 子句。
try 语句按照如下方式工作;
首先,执行
try子句(在关键字try和关键字except之间的语句)如果没有异常发生,忽略
except子句,try子句执行后结束。如果在执行
try子句的过程中发生了异常,那么try子句余下的部分将被忽略。如果异常的类型和
except之后的名称相符,那么对应的except子句将被执行。执行
try语句之后的代码。
如果一个异常没有与任何的
except匹配,那么这个异常将会传递给上层的try中。
一个 try 语句可能包含多个 except
子句,分别来处理不同的特定的异常。最多只有一个分支会被执行。
处理程序将只针对对应的 try 子句中的异常进行处理,而不是其他的
try 的处理程序中的异常。
一个 except
子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组,例如:
except (RuntimeError, TypeError, NameError):
pass
最后一个 except
子句可以忽略异常的名称,它将被当作通配符使用。你可以使用这种方法打印一个错误信息,然后再次把异常抛出。
>>> import sys
>>>
>>> try:
>>> f = open('myfile.txt')
>>> s = f.readline()
>>> i = int(s.strip())
>>> except OSError as err:
>>> print("OS error: {0}".format(err))
>>> except ValueError:
>>> print("Could not convert data to an integer.")
>>> except:
>>> print("Unexpected error:", sys.exc_info()[0])
>>> raise
OS error: [Errno 2] No such file or directory: 'myfile.txt'
3.6.5. else 子句¶
try except 语句还有一个可选的 else
子句,如果使用这个子句,那么必须放在所有的 except 子句之后。
这个子句将在 try 子句没有发生任何异常的时候执行。 例如:
>>> for arg in sys.argv[1:]:
>>> try:
>>> f = open(arg, 'r')
>>> except IOError:
>>> print('cannot open', arg)
>>> else:
>>> print(arg, 'has', len(f.readlines()), 'lines')
>>> f.close()
cannot open -f
/home/bk/.local/share/jupyter/runtime/kernel-551e78aa-2f53-48c5-84a6-ef4a5f5b306f.json has 12 lines
使用 else 子句比把所有的语句都放在 try
子句里面要好,这样可以避免一些意想不到的、而 except
又没有捕获的异常。
3.6.6. 其他¶
上面已经看到,异常处理并不仅仅处理那些直接发生在 try
子句中的异常,而且还能处理子句中调用的函数(甚至间接调用的函数)里抛出的异常。例如:
>>> def this_fails():
>>> x = 1/0
>>>
>>> try:
>>> this_fails()
>>> except ZeroDivisionError as err:
>>> print('Handling run-time error:', err)
Handling run-time error: division by zero