一、异常
即使语句或表达式在语法上是正确的,但在尝试执行时,它仍可能会引发错误。 在执行时检测到的错误被称为 异常,异常不一定会导致严重后果
1.1 内置异常
在 Python 中,所有异常必须为一个派生自 BaseException
的类的实例
内置异常表
BaseException 所有异常的基类
+-- SystemExit 解释器请求退出
+-- KeyboardInterrupt 用户中断执行(通常是输入^C)
+-- GeneratorExit 生成器(generator)发生异常来通知退出
+-- Exception 常规错误的基类
+-- StopIteration 迭代器没有更多值
+-- StopAsyncIteration 必须通过异步迭代器对象的__anext__()方法引发以停止迭代
+-- ArithmeticError 所有数值计算错误的基类
| +-- FloatingPointError 浮点计算错误
| +-- OverflowError 数值运算超出最大限制
| +-- ZeroDivisionError 除(或取模)零 (所有数据类型
+-- AssertionError 断言语句失败
+-- AttributeError 对象没有这个属性
+-- BufferError 与缓冲区相关的操作时引发
+-- EOFError 没有内建输入,到达EOF 标记
+-- ImportError 导入失败
| +-- ModuleNotFoundError 找不到模块
+-- LookupError 无效数据查询的基类
| +-- IndexError 序列中没有此索引(index)
| +-- 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 Python 语法错误
| +-- IndentationError 缩进错误
| +-- TabError Tab 和空格混用
+-- SystemError 一般的解释器系统错误
+-- TypeError 对类型无效的操作
+-- ValueError 传入无效的参数
| +-- UnicodeError Unicode 相关的错误
| +-- UnicodeDecodeError Unicode 解码时的错误
| +-- UnicodeEncodeError Unicode 编码时错误
| +-- UnicodeTranslateError Unicode 转换时错误
+-- Warning 警告的基类
+-- DeprecationWarning 关于被弃用的特征的警告
+-- PendingDeprecationWarning 关于构造将来语义会有改变的警告
+-- RuntimeWarning 可疑的运行行为的警告
+-- SyntaxWarning 可疑的语法的警告
+-- UserWarning 用户代码生成的警告
+-- FutureWarning 有关已弃用功能的警告的基类
+-- ImportWarning 模块导入时可能出错的警告的基类
+-- UnicodeWarning 与Unicode相关的警告的基类
+-- BytesWarning bytes和bytearray相关的警告的基类
+-- ResourceWarning 与资源使用相关的警告的基类
二、处理异常
可以编写处理所选异常的程序
2.1 捕获一个异常
try:
x = int(input("Please enter a number: "))
except ValueError:
print("Oops! That was no valid number. Try again...")
- 首先,执行
try
子句 (try
和except
关键字之间的(多行)语句) - 如果没有异常发生,则跳过
except
子句 并完成try
语句的执行 - 如果在执行
try
子句时发生了异常,则跳过该子句中剩下的部分。然后,如果异常的类型和except
关键字后面的异常匹配,则执行except
子句 ,然后继续执行try
语句之后的代码 - 如果发生的异常和
except
子句中指定的异常不匹配,则将其传递到外部的try
语句中;如果没有找到处理程序,则它是一个 未处理异常,执行将停止并显示如上所示的消息
2.2 捕获多个异常
try:
x = int(input("Please enter a number: "))
except (RuntimeError, TypeError, NameError, ValueError):
pass
- 一个
try
语句可能有多个except
子句,以指定不同异常的处理程序。 最多会执行一个处理程序。 处理程序只处理相应的try
子句中发生的异常,而不处理同一try
语句内其他处理程序中的异常。 一个except
子句可以将多个异常命名为带括号的元组
2.3 异常是捕获的同一个类或基类
如果发生的异常和 except
子句中的类是同一个类或者是它的基类,则异常和 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")
- 请注意如果
except
子句被颠倒(把except B
放到第一个),它将打印 B,B,B --- 即第一个匹配的 except 子句被触发。
2.4 省略异常名
最后的 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.5 可选的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()
- 它避免了意外捕获由
try ... except
语句保护的代码未引发的异常。
2.6 为异常参数
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)
输出:
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
2.7 调用异常
异常处理程序不仅处理 try
子句中遇到的异常,还处理 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
三、抛出异常(raise
)
raise
语句允许程序员强制发生指定的异常
raise NameError('HiThere')
raise
唯一的参数就是要抛出的异常。这个参数必须是一个 异常实例 或者是一个 异常类(派生自 Exception 的类)。- 如果传递的是一个异常类,它将通过调用没有参数的构造函数来隐式实例化
四、用户自定义异常
程序可以通过创建新的异常类来命名它们自己的异常。异常通常应该直接或间接地从 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
- 大多数异常都定义为名称以“Error”结尾,类似于标准异常的命名。
- 许多标准模块定义了它们自己的异常,以报告它们定义的函数中可能出现的错误
五、清理操作
try
语句有另一个可选子句(finally
),用于定义必须在所有情况下执行的清理操作
- 如果存在
finally
子句,则finally
子句将作为try
语句结束前的最后一项任务被执行 - finally 子句不论 try 语句是否产生了异常都会被执行
>>> try:
... raise KeyboardInterrupt
... finally:
... print('Goodbye, world!')
...
Goodbye, world!
KeyboardInterrupt
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
发生异常finally
执行情况
- 如果异常没有被某个
except
子句所处理,则该异常会在finally
子句执行之后被重新引发 - 异常也可能在
except
或else
子句执行期间发生。 同样地,该异常会在 finally 子句执行之后被重新引发。 finally
子句将在执行break
,continue
或return
语句之前被执行- 如果
finally
子句中包含一个return
语句,则返回值将来自finally
子句的某个return
语句的返回值,而非来自try
子句的return
语句的返回值