Python中的异常
1. 概述
使用try…except语句块处理异常。
try语句的工作原理是:
- 首先,执行 try 子句 (try 和 except 关键字之间的(多行)语句)。
- 如果没有异常发生,则跳过 except 子句 并完成 try 语句的执行。
- 如果在执行 try 子句时发生了异常,则跳过该子句中剩下的部分。 然后,如果异常的类型和 except 关键字后面的异常匹配,则执行 except 子句,然后继续执行 try 语句之后的代码。
- 如果发生的异常和 except 子句中指定的异常不匹配,则将其传递到外部的 try 语句中;如果没有找到处理程序,则它是一个 未处理异常,执行将停止。
一个 try 语句可能有多个 except 子句,以指定不同异常的处理程序,最多只会执行一个处理程序。处理程序只处理相应的 try 子句中发生的异常,而不处理同一 try 语句内其他处理程序中的异常。
最后的 except 子句可以省略异常名,以用作通配符。
2. 处理异常
2.1 一个except语句只处理一个异常(如ValueError异常)
while True:
try:
x = int(input('Please enter a number: '))
break
except ValueError:
print("Oops! That was no valid number. Try again...")
输出:
D:\Work\PRJS\pythonProject\pythonProject4\venv\Scripts\python.exe D:/Work/PRJS/pythonProject/pythonProject4/venv/MyException.py
Please enter a number: d
Oops! That was no valid number. Try again...
Please enter a number: 12
Process finished with exit code 0
2.2 一个except语句处理多个异常
一个 except 子句可以将多个异常命名为带括号的元组,即同时处理多个异常。
while True:
try:
x = int(input('Please enter a number: '))
break
except (RuntimeError, TypeError, NameError,ValueError):
print("Oops! That was no valid number. Try again...")
2.3 处理有继承关系的异常类
如果发生的异常和 except 子句中的类是同一个类或者是它的基类,则异常和 except 子句中的类是兼容的;但是反过来不成立,except子句中的派生类不能处理来自基类的异常。
class ExceptionBase(Exception):
pass
class ExceptionA(ExceptionBase):
pass
class ExceptionB(ExceptionA):
pass
for cls in [ExceptionBase, ExceptionA, ExceptionB]:
try:
raise cls()
except ExceptionB:
print("ExceptionB")
except ExceptionA:
print("ExceptionA")
except ExceptionBase:
print("ExceptionBase")
输出:
D:\Work\PRJS\pythonProject\pythonProject4\venv\Scripts\python.exe D:/Work/PRJS/pythonProject/pythonProject4/venv/MyException.py
ExceptionBase
ExceptionA
ExceptionB
Process finished with exit code 0
2.3 except通配符处理所有未处理的异常
最后的 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
2.4 try … except 语句可选的 else 子句
可选的 else 子句,在使用时必须放在所有的 except 子句后面。在 try 子句不引发异常时才会执行的else子句的代码。
class ExceptionBase(Exception):
pass
class ExceptionA(ExceptionBase):
pass
class ExceptionB(ExceptionA):
pass
for cls in [ExceptionBase, ExceptionA, ExceptionB]:
try:
raise cls()
except ExceptionB:
print("ExceptionB")
except ExceptionA:
print("ExceptionA")
except ExceptionBase:
print("ExceptionBase")
else:
print('There is no exception')
输出:
D:\Work\PRJS\pythonProject\pythonProject4\venv\Scripts\python.exe D:/Work/PRJS/pythonProject/pythonProject4/venv/MyException.py
ExceptionBase
ExceptionA
ExceptionB
Process finished with exit code 0
2.5 try … except 语句可选的 finally 子句
finally子句在try…except执行完后,最后必执行。
finally子句执行的情况:
- 如果在执行 try 子句期间发生了异常,该异常可由一个 except 子句进行处理。 如果异常没有被某个 except 子句所处理,则该异常会在 finally 子句执行之后被重新引发。
- 异常也可能在 except 或 else 子句执行期间发生。 同样地,该异常会在 finally 子句执行之后被重新引发。
- 如果在执行 try 语句时遇到一个 break, continue 或 return 语句,则 finally 子句将在执行 break, continue 或 return 语句之前被执行。
- 如果 finally 子句中包含一个 return 语句,则返回值将来自 finally 子句的某个 return 语句的返回值,而非来自 try 子句的 return 语句的返回值。
try:
raise NameError('HiThere')
except NameError:
print('An exception flew by!')
else:
print('There is no exception')
finally:
print('-----finally-----')
输出:
D:\Work\PRJS\pythonProject\pythonProject4\venv\Scripts\python.exe D:/Work/PRJS/pythonProject/pythonProject4/venv/MyException.py
An exception flew by!
-----finally-----
Process finished with exit code 0
2.6 带参数的异常
except 子句可以在异常名称后面跟 as 关键字指定一个异常类的实例,它的参数存储在 instance.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)
else:
print('There is no exception')
输出:
D:\Work\PRJS\pythonProject\pythonProject4\venv\Scripts\python.exe D:/Work/PRJS/pythonProject/pythonProject4/venv/MyException.py
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
Process finished with exit code 0
3. 抛出异常
使用关键字raise 抛出异常。
raise 唯一的参数就是要抛出的异常,这个参数必须是一个异常实例或者是一个异常类(派生自 Exception 的类)。如果传递的是一个异常类,它将通过调用没有参数的构造函数来隐式实例化:
raise ValueError # shorthand for 'raise ValueError()'
如果你需要确定是否引发了异常但不打算处理它,则可以使用更简单的 raise 语句形式重新引发异常:
try:
raise NameError('HiThere')
except NameError:
print('An exception flew by!')
raise
输出:
D:\Work\PRJS\pythonProject\pythonProject4\venv\Scripts\python.exe D:/Work/PRJS/pythonProject/pythonProject4/venv/MyException.py
An exception flew by!
Traceback (most recent call last):
File "D:/Work/PRJS/pythonProject/pythonProject4/venv/MyException.py", line 2, in <module>
raise NameError('HiThere')
NameError: HiThere
Process finished with exit code 1
4. 自定义异常类
程序可以通过创建新的异常类来命名它们自己的异常,异常通常应该直接或间接地从 Exception 类派生。
可以定义异常类,它可以执行任何其他类可以执行的任何操作,但通常保持简单,只提供一些属性,这些属性允许处理程序为异常提取有关错误的信息。
在创建可能引发多个不同错误的模块时,通常的做法是为该模块定义的异常创建基类,并为不同错误条件创建特定异常类的子类:
class Error(Exception):
"""Base class for exceptions in this module."""
pass
class InputError(Error):
"""Exception raised for errors in the input.
Attributes:
expression -- input expression in which the error occurred
message -- explanation of the error
"""
def __init__(self, expression, message):
self.expression = expression
self.message = message
class TransitionError(Error):
"""Raised when an operation attempts a state transition that's not
allowed.
Attributes:
previous -- state at beginning of transition
next -- attempted new state
message -- explanation of why the specific transition is not allowed
"""
def __init__(self, previous, next, message):
self.previous = previous
self.next = next
self.message = message