Python语法速查: 10. 异常

返回目录

 

本篇索引

(1)内置异常

(2)自定义异常

(3)主动引发异常

(4)捕捉异常

(5)error模块

(6)with语句

 

 

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

 

● 使用 contextlib 模块

使用 contextlib 模块可以更加方便地编写上例那样的支持 With 上下文管理语句的类。 它提供了一个 @contextmanager 装饰器,用户使用它装饰某个“函数”,达到上面类似的效果。 下面是一个使用 @contextmanager 装饰器的例子:

from contextlib import contextmanager
@contextmanager
def ListTransaction(thelist):
    workingcopy = list(thelist)
    yield workingcopy
    # 仅在没有出现错误时,才会修改原始列表
    thelist[:] = workingcopy

上面的程序中,装饰器将传递给 yield 的值用作了 __enter__() 方法的返回值(即提供给外部的 With 语句)。在退出 With (即调用 __exit__() 方法)时,执行将在 yield 语句后恢复 (即把原始的 thelist 列表的值改掉)。

如果 With 上下文中引发了异常,它将以异常形式出现在生成器函数中。

 

 

 

 

返回目录

 

posted @ 2020-01-04 09:33  初级电路与软件研究  阅读(939)  评论(0编辑  收藏  举报