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