一、何谓异常处理
在我们调试程序时,经常不可避免地出现意料之外的情况,导致程序不得不停止运行,然后提示大堆提示信息,大多是这种情况都是由异常引起的。异常的出现一方面是因为写代码时粗心导致的语法错误,这种错误在程序编译时就可以发现;另一方面也可能是因为程序逻辑错误,这种错误往往是不可避免地,只能通过异常处理来防止程序退出。
二、异常类型
Python自带的异常处理机制非常强大,提供了很多内置异常类,可向用户准确反馈出错信息。Python是面向对象语言,认为一切皆对象,所以异常也是对象。Python异常处理机制中的BaseException是所有内置异常的基类,但用户定义的类并不直接继承BaseException,所有的异常类都是从Exception继承,且都在exceptions模块中定义。Python自动将所有异常名称放在内建命名空间中,所以程序不必导入exceptions模块即可使用异常。
Python内置异常类继承层次结构如下:
BaseException # 所有异常的基类 -- SystemExit # 解释器请求退出 -- KeyboardInterrupt # 用户中断执行(通常是输入^C) -- GeneratorExit # 生成器(generator)发生异常来通知退出 -- Exception # 常规异常的基类 -- StopIteration # 迭代器没有更多的值 -- StopAsyncIteration # 必须通过异步迭代器对象的__anext__()方法引发以停止迭代 -- ArithmeticError # 各种算术错误引发的内置异常的基类 | -- FloatingPointError # 浮点计算错误 | -- OverflowError # 数值运算结果太大无法表示 | -- ZeroDivisionError # 除(或取模)零 (所有数据类型) -- AssertionError # 当assert语句失败时引发 -- AttributeError # 属性引用或赋值失败 -- BufferError # 无法执行与缓冲区相关的操作时引发 -- EOFError # 当input()函数在没有读取任何数据的情况下达到文件结束条件(EOF)时引发 -- ImportError # 导入模块/对象失败 | -- ModuleNotFoundError # 无法找到模块或在在sys.modules中找到None -- LookupError # 映射或序列上使用的键或索引无效时引发的异常的基类 | -- IndexError # 序列中没有此索引(index) | -- KeyError # 映射中没有这个键 -- MemoryError # 内存溢出错误(对于Python 解释器不是致命的) -- NameError # 未声明/初始化对象 (没有属性) | -- UnboundLocalError # 访问未初始化的本地变量 -- OSError # 操作系统错误,EnvironmentError,IOError,WindowsError,socket.error,select.error和mmap.error已合并到OSError中,构造函数可能返回子类 | -- BlockingIOError # 操作将阻塞对象(e.g. socket)设置为非阻塞操作 | -- ChildProcessError # 在子进程上的操作失败 | -- ConnectionError # 与连接相关的异常的基类 | | -- BrokenPipeError # 另一端关闭时尝试写入管道或试图在已关闭写入的套接字上写入 | | -- ConnectionAbortedError # 连接尝试被对等方中止 | | -- ConnectionRefusedError # 连接尝试被对等方拒绝 | | -- ConnectionResetError # 连接由对等方重置 | -- FileExistsError # 创建已存在的文件或目录 | -- FileNotFoundError # 请求不存在的文件或目录 | -- InterruptedError # 系统调用被输入信号中断 | -- IsADirectoryError # 在目录上请求文件操作(例如 os.remove()) | -- NotADirectoryError # 在不是目录的事物上请求目录操作(例如 os.listdir()) | -- PermissionError # 尝试在没有足够访问权限的情况下运行操作 | -- ProcessLookupError # 给定进程不存在 | -- TimeoutError # 系统函数在系统级别超时 -- ReferenceError # weakref.proxy()函数创建的弱引用试图访问已经垃圾回收了的对象 -- 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 # 与资源使用相关的警告的基类。被默认警告过滤器忽略。
三、异常捕获与处理
语法: try: ... except [异常名] as e: ...
示例:
name = ["alex","jack"] data = {} try: a=5 f = open("123","r") #会触发异常Exception,这个包括了所有个异常 name[3] # 会触发异常indexError data["name"] # 会触发异常KeyError except KeyError as e: print("没有此信息",e) except IndexError as e: print("列表索引超出范围",e) except Exception as e: print("未知错误",e) else: # 一切正常的情况下会执行后面的语句 print("OK") finally: print("不管有没有错误,都会执行的语句") #输出: 未知错误 [Errno 2] No such file or directory: '123' 不管有没有错误,都会执行的语句 #上面的输出结果是因为,按照执行的顺序,首先执行打开文件出现异常,后面的语句没有被执行的机会。 想要一条语句抓住两种错误,可以这样写: try: name[3] data["name"] except (KeyError,IndexError) as e: print("没有此信息",e)
需要注意的是:
- try - except虽然结构简单,但可能捕获与程序无关、意料之外的系统异常,而且可能意外拦截其他处理器的异常。例如,在Python中,即表示系统离开调用(sys.exit())也会出发异常,然而这种异常我们通常不需要捕获。所以,这种结构尽量避免使用,而使用 <try: ... except [异常名] as e: ...>这种结构。
- Exception类是所有Python异常类的父类,所以except Exception将可以捕获任何异常,换句话说,它是万能异常处理句式。
- 有一种错误没办法抓住:缩进错误,例如:
try: name1 = 10 b = 20 except Exception as e: print("错误!") # output: File "F:/Python Files/Learning Log/day7/异常处理.py", line 44 b = 20 ^ IndentationError: unexpected indent
四、raise(主动抛出异常)
有时候,异常可以作为代码运行的标志,通过主动触发异常可以改变代码的运行路线,从而提高代码健壮性。主动触发异常需使用raise关键字,其语法结构如下:
raise [Exception [, args [, traceback]]]
示例:
def fun(x , y): try: print('fun()方法开始执行……') if isinstance(x,int) and isinstance(y,int): return x+y else: raise TypeError('类型错误') except Exception as e: print(e) finally: print('fun()方法执行结束……') fun(2 , '3') # output: fun()方法开始执行…… 类型错误 fun()方法执行结束……
五、assert(断言)
根据assert后面的表达式的真假来控制程序流。 asset语法结构如下:
assert expression,'information'
若为expression结果为True,则往下执行。若为False,则中断程序并调用默认的异常处理器抛出AssertionError异常,同时输出指定的提示信息。
class A(object): def __init__(self,name): self.name = name a = A("Alex") try: assert type(a.name) is str # 意为:断定A.name的类型是字符串 except AssertionError: print("is not str")
断言的作用是:可以做一些检查,增强安全性
六、with/as上下文管理器
with/as语句通常是作为try/finally语句的替代方案,不过with/as更加优雅。在有一些任务中,可能事先需要设置,然后不管在任务过程中是否顺利(有无异常抛出),最后后做清理工作。对于这种场景, with/as语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读写数据,但不管读写数据是否有异常发生,最后都要关闭文件句柄。
with/as语句的基本格式如下:
with expression [as variable] : with-block
在这里的expression会返回一个对象,as子句是可选的,当存在as子句时,expression返回的对象会赋值给variable。
使用with/as语句将一段字符串写入文件:
with open('data.txt' , 'w') as myfile: myfile.write('123456789') 不适用with/as语句,如果要实现同样的效果,只能这么写: try: myfile = open('data.txt' , 'w') myfile.write('123456789') except Exception as e: print(e) finally: myfile.close()
七、自定义异常类
如果Python提供的内置异常内不满足使用要求,那么,可以自定义一个异常类,自定义异常类必须继承Exception类。
class AlexException(Exception): def __init__(self,msg): self.message = msg try: raise AlexException("数据库连不上") # raise 主动触发异常 except AlexException as e: print(e) # output: 数据库连不上
需要注意:
- 自定义的异常不会自动触发,所以需要主动触发。会自动触发的都是标准的异常
- 自定义的异常不能和原有的异常重名
八、总结
Python异常处理机制总结:
- Python异常处理的常用内置类
- 异常捕获/处理句式结构
- 主动触发异常,断言
- with上下文管理协议
- 自定义异常类
在我们使用异常处理后,若有异常发生,我们需要查看错误的详细信息,以及在哪一行出的错误,需要使用 traceback 模块
import traceback try: ...... except Exception as e: traceback.print_exec()