Python语法速查: 10. 异常
本篇索引
(1)
(2)
(3)
(4)
(5)
(6)
(1)内置异常
● 异常的基类:
以下这些异常作为具体异常的基类,都不会被显式引发,但是可以使用它们捕捉某种错误。
基类名称 | 说明 | ||
---|---|---|---|
baseexception |
所有内置异常的基类,其他所有内置异常都派生自该类。 |
||
exception | 所有内置的非系统退出异常都派生自此类(即除了:systemexit, generatorexit, keyboardinterrupt之外的所有内置异常)。所有用户自定义异常也应当派生自此类。 | ||
arithmeticerror | 算术运算异常的基类,包括溢出、除零等等。 | ||
lookuperror | 索引和键错误的基类。 | ||
buffererror | 当与缓冲区相关的操作无法执行时引发,一般由用户自行继承使用。 |
● 具体异常
以下异常属于经常被引发的异常。
具体异常名称 | 说明 |
---|---|
以下异常直接继承自:baseexception | |
generatorexit | 由生成器的.close()方法引发。 |
keyboardinterrupt | 由键盘中断(通常为 ctrl-c)生成。 |
systemexit | 程序退出,一般系统函数sys.exit()引发。 |
以下异常继承自:baseexception -> exception | |
stopiteration | 引发后可停止迭代。 |
stopasynciteration | 由异步迭代器引发停止迭代。 |
assertionerror | 当 assert 语句失败时引发。 |
attributeerror | 当属性引用或属性赋值失败时引发。 |
eoferror | 当 input() 函数未读取任何数据即达到文件结束条件 (eof) 时将被引发。(文件操作的诸如read()和readline()方法等i/o操作在遇到eof时会返回空字符串,而不是引发异常) |
importerror | 当 import 语句无法找到模块或者 from 无法在模块中找到名称时引发。python3.3版本后,对此异常添加了name和path属性,用来表示“尝试导入的模块名称”和“触发异常的文件所在的路径”。 |
modulenotfounderror | importerror的子类,python3.6版本新加入。当一个模块无法找到时由import引发。 |
memoryerror | 可用内存不足(但仍可挽救时)时将被引发。 |
nameerror | 在局部或全局命名空间中未找到某个名称时引发。 |
unboundlocalerror | nameerror的子类。引用了未绑定的局部变量时引发。 |
oserror | python3.3起,以下都是oserror的别名:ioerror、environmenterror、windowserror(仅限windows中)。 操作系统错误,主要由os模块中的函数引起。 |
blockingioerror | oserror的子类。当一个操作阻塞一个设置为非阻塞操作的对象时引发。 |
childprocesserror | oserror的子类。当对子进程进行操作失败时将引发,对应于linux系统调用时的errno中的 echild |
connectionerror | 与连接相关问题的基类。 |
brokenpipeerror | connectionerror的子类。当试图写入另一端已被关闭的管道,或是试图写入已关闭写入的套接字时引发。对应于linux系统调用时的errno中的 epipe 和 eshutdown。 |
connectionabortederror | connectionerror的子类。当连接尝试被对端中止时将被引发。对应于linux系统调用时的errno中的 econnaborted。 |
connectionrefusederror | connectionerror的子类。当连接尝试被对端拒绝时将被引发。对应于linux系统调用时的errno中的 econnrefused。 |
connectionreseterror | connectionerror的子类。当连接被对端重置时将被引发。对应于linux系统调用时的errno中的 econnreset。 |
fileexistserror | oserror的子类。当试图创建一个已存在的文件或目录时将被引发。对应于linux系统调用时的errno中的 eexist。 |
filenotfounderror | oserror的子类。当所请求的文件或目录不存在时引发。对应于linux系统调用时的errno中的 enoent。 |
interruptederror | oserror的子类。当系统调用被输入信号中断时引发。对应于linux系统调用时的errno中的 eintr。 |
isadirectoryerror | oserror的子类。当请求对一个目录执行文件操作时引发。对应于linux系统调用时的errno中的 eisdir。 |
notadirectoryerror | oserror的子类。当请求对一个非目录对象执行目录操作时引发。对应于linux系统调用时的errno中的 enotdir。 |
permissionerror | oserror的子类。当没有对相应文件操作权限的时候引发。对应于linux系统调用时的errno中的 eaccess和 eperm |
processlookuperror | oserror的子类。当给定的进程不存在时引发。对应于linux系统调用时的errno中的 esrch。 |
timeouterror | oserror的子类。当一个系统函数发生系统级超时的情况下将被引发。对应于linux系统调用时的errno中的 etimedout。 |
referenceerror | 在弱引用访问某个已被垃圾回收的属性时引发,关于弱引用可参见werkref模块。 |
runtimeerror | 当检测到一个不属于任何其他类别的错误时引发。 |
notimplementederror | runtimeerror的子类。当基类的抽象方法需要派生类实现,而派生类未实现时引发。 |
recursionerror | runtimeerror的子类。python解释器检测发现超过最大递归深度时引发。 |
syntaxerror | 解析器语法错误。 |
indentationerror | syntaxerror的子类。缩进错误时引发。 |
taberror | indentationerror的子类。当缩进包含对制表符和空格符不一致的使用时引发。 |
systemerror | python解释器中的内部错误。 |
typeerror | 当一个操作或函数被应用于类型不适当的对象时引发。 |
valueerror | 当操作或函数接收到具有正确类型但值不适合的参数时引发。 |
unicodeerror | valueerror的子类。unicode编码或解码错误时引发。 |
unicodeencodeerror | unicodeerror的子类。unicode编码错误。 |
unicodedecodeerror | unicodeerror的子类。unicode解码错误。 |
unicodetranslateerror | unicodeerror的子类。在转换过程中产生的与unicode相关的错误。 |
以下异常继承自:baseexception -> exception -> arithmeticerror | |
floatingpointerror | 目前未被使用。 |
overflowerror | 当一个运算结果大到无法表示时将被引发。 |
zerodivisionerror | 当除法或取余运算的第二个参数为零时将被引发。 |
以下异常继承自:baseexception -> exception -> lookuperror | |
indexerror | 序列的下标超出范围时引发。 |
keyerror | 映射(字典)中未找到键时引发。 |
以下警告继承自:baseexception -> exception -> warning | |
deprecationwarning | 与已弃用特性相关警告的基类。 |
pendingdeprecationwarning | 对于已过时并预计在未来弃用,但目前尚未弃用的特性相关警告的基类。 |
runtimewarning | 与模糊的运行时行为相关的警告的基类。 |
syntaxwarning | 与模糊的语法相关的警告的基类。 |
userwarning | 用户代码所产生警告的基类。 |
futurewarning | 与已弃用特性相关警告的基类。 |
importwarning | 与在模块导入中可能的错误相关的警告的基类。 |
unicodewarning | 与 unicode 相关的警告的基类。 |
byteswarning | 与 bytes 和 bytearray 相关的警告的基类。 |
resourcewarning | 与资源使用相关的警告的基类。 会被默认的警告过滤器忽略。 |
(2)自定义异常
可以通过继承exceptions类而定义自己的异常。定义完之后,可以使用raise语句引发这个新的异常,如下例所示:
class networkerror(exception): pass raise networkerror('cannot find host')
下例显示了如何在自定义异常中带有多个异常值:
class networkerror(exception): def __init__(self, errno, msg): self.args = (errno, msg) # 赋值给args是必须的,否则用户无法看到自定义异常的任何细节提示信息 self.errno = errno self.msg = msg raise networkerror(1, 'no response')
使用继承机制将异常组成一个层次结构:
class networkerror(exception): pass class hostnameerror(networkerror): pass class timeouterror(networkerror): pass # 以下为引发并捕捉自定义异常 try: raise timeouterror('time out') except networkerror as e: if type(e) is timeouterror: pass
(3)主动引发异常
● assert(断言)
断言的基本目的是:与其让程序在在将来不知某个时候崩溃,不如在程序中不符合某个预判条件时,主动让程序崩溃。 当断言条件不满足时,会引发assertionerror异常。assert的格式为:
assert condition [,msg]
其中,condition是一个表达式,其值若为false时,assert语句就会引发assertionerror异常。 可以在assert语句后添加字符串msg,用来在发生异常时,提示预先设置的字符串信息。
使用举例:
age = -1 assert 0 < age < 100 'the age is fantastic.' # 执行本句时,会引发assertionerror异常,并会提示:the age is fantastic.
assert语句常和 if __debug__语句一起使用。在调试模式中,只读变量__debug__的值为true,可以随意地在代码中加入assert和调试检查。 在最优模式中(通过-o指定),__debug__为false,将省略所有这些额外的检查。
● raise
使用raise语句可主动引发异常,raise语句的格式为:
raise exception([value])
如果raise语句没有带任何参数,将会再次引发最近一次生成的异常。
raise使用举例:
raise keyerror('abc') # 引发keyerror异常,并提示字符串 'abc'
(4)捕捉异常
通常,使用捕捉异常的代码结构要比使用多个if-else语句判断来得更清晰,而且执行效率也几乎没什么损失,故应该在程序中尽可能使用try/except语句来查错。 一般except语句捕捉并处理完异常后,程序将继续执行跟在最后一个except代码块中的语句,程序并不会返回发生异常时的位置。
如果异常在函数内引发而未被处理,它就会向上传递到函数调用的地方,如果在那里也没有被处理,就会继续向上传递,直到主程序(全局作用域)。 如果主程序里也没有处理,程序会带着堆栈跟踪停止。
如果需要,也可以把未捕捉的异常传递给用户自定义的函数sys.excepthook()进行处理。
● try-except结构:
捕捉单个异常:
try: x = 2/0 except zerodivisionerror: print 'the divisor is zero'
捕捉多个异常:
# 方法一: try: x = 2/'a' except zerodivisionerror: pass except typeerror: pass # 方法二: try: x = 2/'a' except (zerodivisionerror, typeerror, nameerror): print('oops!')
捕捉所有异常:
try: x = 2/0 except exception: # 这里使用基类exception可捕捉除了“键盘中断”和“程序退出”的所有异常 pass
捕捉异常并访问异常对象:
try: x = 2/0 except exception as e: print(e)
这里生成的异常实例e具有一些标准属性,列举如下:
e.args:引发异常时提供的参数元组,一般包含有描述该错误的字符串。
e.__cause__:使用显式关联异常时的前一个异常。
e.__context__:使用隐式关联异常时的前一个异常。
e.__traceback__:与异常相关的跟踪对象。
● try-except-else结构
当try中的语句没有发生异常请跨下,运行else语句块中内容。
示例:
try: f = open('foo', 'r') except ioerror as e: print(e) else: data = f.read() f.close()
● try-except-finally结构
finally语句块用于无论try是否有异常,都要运行的代码。如果没有引发异常,finally子句中的代码将在try的代码块执行完后立即执行。 如果捕捉到了异常,则finally中的内容先运行,然后再运行except语句块中的内容。
示例:
try: f = open('foo', 'r') data = f.read() except ioerror as e: print(e) finally: f.close() # 无论前面发生什么,都会关闭文件
● try-except-else-finally结构
else和finally也可以组合在一起使用。
示例:
try: f = open('foo', 'r') except ioerror as e: print(e) else: data = f.read() finally: f.close()
(5)error模块
errno模块为各种操作系统调用返回的整数错误代码定义了符号名称,这些整数代码通常可在oserror(别名ioerror)异常的errno属性中找到。
os.strerror()函数可以将整数的错误代码转换为字符串信息。
errno模块的errorcode字典,记录了当前操作系统支持的错误代码和posix符号名称的对应关系,下表列举了部分常用的错误代码。
oserror中的错误代码信息:
try: f = open(r'xxxxxx', 'r') except exception as e: print(e.errno) print(e.args) # 运行结果为: 2 (2, 'no such file or directory')
os.strerror()使用示例:
import os import errno print(os.strerror(2)) # 运行结果为错误代码2的字符串含义:no such file or directory'
● 常见posix错误代码
错误代码 | 名称 | 描述 |
---|---|---|
1 | eperm | 操作未得到许可 |
2 | enoent | 文件或目录不存在 |
3 | esrch | 进程不存在 |
4 | eintr | 系统调用被中断 |
5 | eio | i/o错误 |
6 | enxio | 设备或地址不存在 |
7 | e2big | 参数列表过长 |
8 | enoexec | 访问被拒绝 |
9 | ebadf | 错误的文件编号 |
10 | echild | 无子进程 |
11 | eagain | 再试 |
12 | enomem | 内存不足 |
13 | eaccess | 访问被拒绝 |
14 | efault | 错误的地址 |
15 | enotblk | 需要块设备 |
16 | ebusy | 设备或资源方面 |
17 | eexist | 文件存在 |
18 | exdev | 跨设备链接 |
19 | enodev | 没有这个设备 |
20 | enotdir | 不是一个目录 |
21 | eisdir | 是一个目录 |
22 | einval | 无效参数 |
23 | enfile | 文件表溢出 |
24 | emfile | 打开文件过多 |
25 | enotty | 不是一个终端 |
26 | etxtbsy | 文本文件忙 |
27 | efbig | 文件过大 |
28 | enospc | 设备上无剩余空间 |
29 | espipe | 非法寻址 |
30 | erofs | 只读文件系统 |
31 | emlink | 链接过多 |
32 | epipe | 管道已损坏 |
33 | edom | 数学参数在函数作用域之外 |
34 | erange | 无法表示的数学结果 |
35 | edeadlock | 文件锁定死锁错误 |
36 | enametoolong | 文件名过长 |
37 | enolck | 无可用记录锁定 |
38 | enosys | 函数无法实现 |
39 | enotempty | 目录不为空 |
40 | eloop | 遇到过多的符号链接 |
84 | eilseq | 非法的字节序列 |
85 | erestart | 中断系统调用需重启 |
86 | estrpipe | 流管道错误 |
87 | eusers | 用户过多 |
88 | enotsock | 非套接字上的套接字操作 |
89 | edestaddrreq | 需要目的地址 |
90 | emsgsize | 消息过长 |
91 | eprototype | 套接字的协议类型错误 |
92 | enoprotoopt | 协议不可用 |
93 | eprotonosupport | 不支持协议 |
94 | esocktnosupport | 套接字类型不受支持 |
95 | enotsup | 操作被远端支持 |
96 | epfnosupport | 不支持协议族 |
97 | eafnosupport | 协议不支持地址族 |
98 | eaddrinuse | 地址已使用 |
99 | eaddrnotavail | 无法分配请求的地址 |
100 | enetdown | 网络已关闭 |
101 | enetunreach | 网络不可到达 |
102 | enetreset | 网络由于重置中断连接 |
103 | econnaborted | 软件导致连接中断 |
104 | econnreset | 对等端已将连接重置 |
105 | enobufs | 无可用缓存空间 |
106 | eisconn | 传输端点已经连接 |
107 | enotconn | 传输端点未连接 |
108 | eshutdown | 无法在传输端点关闭后发送 |
109 | etoomanyrefs | 引用过多:无法连接 |
110 | etimedout | 连接超时 |
111 | econnrefused | 连接被拒绝 |
112 | ehostdown | 主机已关闭 |
113 | ehostunreach | 无路由通向主机 |
114 | ealready | 操作已经在进行中 |
115 | einprogress | 操作正在进行 |
116 | estale | 失效的nfs文件句柄 |
125 | ecanceled | 操作取消 |
126 | enokey | 无此键 |
127 | ekeyexpired | 键过期 |
128 | ekeyrevoked | 键被撤回 |
129 | ekeyrejected | 键被服务拒绝 |
130 | eownerdead | 拥有者已不存在 |
131 | enotrecoverable | 状态不可恢复 |
132 | erfkill | 操作由于rf-kill无法进行 |
(6)with语句
with语句支持在“上下文管理器”对象的控制下,执行一系列语句。常用于管理各种系统资源(如文件、锁、连接等)。 当程序中发生异常,而导致脱离正常的释放资源语句时,只要用了with,就可以保证在离开with语句块时自动释放这些资源。下面是2个简单的例子:
自动关闭文件对象
with open('a.txt', 'w') as f: f.write('xyz\n') ...... f.write('done\n') # 当程序离开with语句块时,with语句会自动关闭已打开的文件
自动释放锁
import threading lock = threading.lock() with lock: ...... # 当程序离开with语句块时,with语句会自动释放这个锁
● with语句的一般语法:
with obj [as var] statements
obj对象需要实现__enter__()方法和__exit__()方法来支持with语句。当执行with obj语句时,会自动调用obj.__enter__()方法, 该方法的返回值将被放入指定变量var中。
当程序离开with语句块时,会自动调用obj.__exit__()方法,其入参形式为:__exit__(type, value, traceback), 三个入参分别为:当前异常的类型、值、跟踪信息。__exit__()方法返回true或false(表示被引发的异常是否得到了处理)
以下为一个用户自定义类支持with的例子,这个类支持对已有列表进行一系列修改, 但这些修改只有在没有发生异常时才会生效,否则原始列表将保持不变。
class listtransaction(object): def __init__(self, thelist): self.thelist = thelist def __enter__(self): self.workingcopy = list(self.thelist) return self.workingcopy def __exit__(self, type, value, tb): if type is none: self.thelist[:] = self.workingcopy return # 使用with语句: items = [1,2,3] try: with listtransaction(items) as working: working.append(4) working.append(5) raise runtimeerror() except runtimeerror: pass print(items) # 由于在离开with语句块时发生了异常,因此__exit__()的入参type不为none,最终结果为:[1,2,3]