Python使用异常对象来表示异常状态,并在遇到错误时引发异常。异常对象未被处理(或捕获)时,程序将终止并显示一条错误消息(traceback)。
异常和函数有着天然的联系。如果不处理函数中引发的异常,它将向上传播到调用函数的地方。如果在那里也未得到处理,异常将继续传播,直至到达主程序(全局作用域)。如果主程序中也没有异常处理程序,程序将终止并显示栈跟踪消息。
触发异常:raise
raise 唯一的参数就是要触发的异常。这个参数必须是异常实例或异常类(派生自 Exception 类)。
raise Exception
# output:
# Traceback (most recent call last):
# File "E:\PyCode\Python_Study\LsitDemo.py", line 1, in <module>
# raise Exception
# Exception
raise Exception('Exception Message')
# output:
# Traceback (most recent call last):
# File "E:\PyCode\Python_Study\LsitDemo.py", line 1, in <module>
# raise Exception('Exception Message')
# Exception: Exception Message
如果只想判断是否触发了异常,但并不打算处理该异常,则可以使用更简单的 raise 语句重新触发异常:
try:
raise NameError('HiThere')
except NameError:
print('An exception flew by!')
raise
# output:
# Traceback (most recent call last):
# File "E:\PyCode\Python_Study\LsitDemo.py", line 2, in <module>
# raise NameError('HiThere')
# NameError: HiThere
# An exception flew by!
异常链
raise 语句支持可选的 from 子句,该子句用于启用链式异常。
# exc must be exception instance or None.
raise RuntimeError from exc
转换异常时,这种方式很有用。例如:
def func():
raise ConnectionError
try:
func()
except ConnectionError as exc:
raise RuntimeError('Failed to open database') from exc
# output:
# Traceback (most recent call last):
# File "E:\PyCode\Python_Study\LsitDemo.py", line 6, in <module>
# func()
# File "E:\PyCode\Python_Study\LsitDemo.py", line 2, in func
# raise ConnectionError
# ConnectionError
#
# The above exception was the direct cause of the following exception:
#
# Traceback (most recent call last):
# File "E:\PyCode\Python_Study\LsitDemo.py", line 8, in <module>
# raise RuntimeError('Failed to open database') from exc
# RuntimeError: Failed to open database
异常链会在 except 或 finally 子句内部引发异常时自动生成。 这可以通过使用 from None
这样的写法来禁用:
try:
open('database.sqlite')
except OSError:
raise RuntimeError from None
# output:
# Traceback (most recent call last):
# File "E:\PyCode\Python_Study\LsitDemo.py", line 4, in <module>
# raise RuntimeError from None
# RuntimeError
内置异常
BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- StopIteration +-- StopAsyncIteration +-- ArithmeticError | +-- FloatingPointError | +-- OverflowError | +-- ZeroDivisionError +-- AssertionError +-- AttributeError +-- BufferError +-- EOFError +-- ImportError | +-- ModuleNotFoundError +-- LookupError | +-- IndexError | +-- KeyError +-- MemoryError +-- NameError | +-- UnboundLocalError +-- OSError | +-- BlockingIOError | +-- ChildProcessError | +-- ConnectionError | | +-- BrokenPipeError | | +-- ConnectionAbortedError | | +-- ConnectionRefusedError | | +-- ConnectionResetError | +-- FileExistsError | +-- FileNotFoundError | +-- InterruptedError | +-- IsADirectoryError | +-- NotADirectoryError | +-- PermissionError | +-- ProcessLookupError | +-- TimeoutError +-- ReferenceError +-- RuntimeError | +-- NotImplementedError | +-- RecursionError +-- SyntaxError | +-- IndentationError | +-- TabError +-- SystemError +-- TypeError +-- ValueError | +-- UnicodeError | +-- UnicodeDecodeError | +-- UnicodeEncodeError | +-- UnicodeTranslateError +-- Warning +-- DeprecationWarning +-- PendingDeprecationWarning +-- RuntimeWarning +-- SyntaxWarning +-- UserWarning +-- FutureWarning +-- ImportWarning +-- UnicodeWarning +-- BytesWarning +-- EncodingWarning +-- ResourceWarning
自定义异常类
直接或间接地继承Exception
class SomeCustomException(Exception): pass
# pass需要更改为所需的代码
异常的处理
try 语句的工作原理如下:
-
如果没有触发异常,则跳过 except 子句,try 语句执行完毕。
-
如果在执行 try 子句时发生了异常,则跳过该子句中剩下的部分。 如果异常的类型与 except 关键字后指定的异常相匹配,则会执行 except 子句,然后跳到 try/except 代码块之后继续执行。
-
如果发生的异常与 except 子句 中指定的异常不匹配,则它会被传递到外部的 try 语句中;如果没有找到处理程序,则它是一个 未处理异常 且执行将终止并输出如上所示的消息。
try 语句可以有多个 except 子句 来为不同的异常指定处理程序。 但最多只有一个处理程序会被执行。 处理程序只处理对应的 try 子句 中发生的异常,而不处理同一 try
语句内其他处理程序中的异常。 except 子句 可以用带圆括号的元组来指定多个异常,例如:
except (RuntimeError, TypeError, NameError):
pass
# pass需要更改为所需的代码
如果发生的异常与 except 子句中的类是同一个类或是它的基类时,则该类与该异常相兼容(反之则不成立 --- 列出派生类的 except 子句 与基类不兼容)。
例如,下面的代码将依次打印 B, C, D,
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")
# output: B
# output: C
# output: D
请注意如果颠倒 except 子句 的顺序(把 except B
放在最前),则会输出 B, B, B --- 即触发了第一个匹配的 except 子句。请注意如果颠倒 except 子句 的顺序(把 except B
放在最前),则会输出 B, B, B --- 即触发了第一个匹配的 except 子句。
class B(Exception):
pass
class C(B):
pass
class D(C):
pass
for cls in [B, C, D]:
try:
raise cls()
except B:
print("B")
except C:
print("C")
except D:
print("D")
# output: B
# output: B
# output: B
如果你就是要使用一段代码捕获所有的异常,只需在except语句中不指定任何异常类即可。
可以选择让最后一个 except 子句省略异常名称,但在此之后异常值必须从 sys.exc_info()
获取。
try ... except 语句具有可选的 else 子句,该子句如果存在,它必须放在所有 except 子句 之后。 它适用于 try 子句 没有引发异常但又必须要执行的代码。使用 else
子句比向 try 子句添加额外的代码要好,可以避免意外捕获非 try
... except
语句保护的代码触发的异常。
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()
except 子句 可以在异常名称后面指定一个变量。 这个变量会绑定到一个异常实例并将参数存储在 instance.args
中。
为了方便起见,该异常实例定义了 __str__()
以便能直接打印参数而无需引用 .args
。 也可以在引发异常之前先实例化一个异常并根据需要向其添加任何属性。:
try:
raise Exception('spam', 'eggs')
except Exception as inst:
print(type(inst)) # the exception instance
print(inst.args) # arguments stored in .args
print(inst) # __str__ allows args to be printed directly,
# but may be overridden in exception subclasses
x, y = inst.args # unpack args
print('x =', x)
print('y =', y)
# output: <class 'Exception'>
# output: ('spam', 'eggs')
# output: ('spam', 'eggs')
# output: x = spam
# output: y = eggs
如果异常有参数,则它们将作为未处理异常的消息的最后一部分('详细信息')打印。
def this_fails():
x = 1/0
try:
this_fails()
except ZeroDivisionError as err:
print('Handling run-time error:', err)
# output: Handling run-time error: division by zero
定义清理操作¶
try 语句还有一个可选子句,用于定义在所有情况下都必须要执行的清理操作。
如果存在 finally 子句,则 finally
子句是 try 语句结束前执行的最后一项任务。不论 try
语句是否触发异常,都会执行 finally
子句。以下内容介绍了几种比较复杂的触发异常情景:
-
如果执行
try
子句期间触发了某个异常,则某个 except 子句应处理该异常。如果该异常没有except
子句处理,在finally
子句执行后会被重新触发。 -
except
或else
子句执行期间也会触发异常。 同样,该异常会在finally
子句执行之后被重新触发。 -
如果执行
try
语句时遇到 break,、continue 或 return 语句,则finally
子句在执行break
、continue
或return
语句之前执行。 -
如果
finally
子句中包含return
语句,则返回值来自finally
子句的某个return
语句的返回值,而不是来自try
子句的return
语句的返回值。