如何控制异常 try-except
出错我们无可避免,代码写错程序当然运行不了。但是即使你代码写对了,你就一定能保证运行成功吗? 当然不是,大千世界,这世界复杂到你处理不过来。如果这个代码完全是你自己写的, 而且确保每个环节都检测到了。那有可能你不会运行出任何错误。 但是这代码你有可能是基于他人的代码,别人没有你这样敬业,他的代码可能会出错呀。
即使别人的代码不出错,但是也有可能物理环境会出错呀,比如 CPU 超了,某人的服务运行失败,你恰好又是基于他提供的服务, 你也跟着超时了。这些都会让你的程序报错。
还好,在 Python 中,或者任何语言中,都有一些处理错误的方式,handle 这些错误,保证你的程序可以顺利执行下去。
我知道你可能报错¶
为了让你的程序兼容性更好,你往往就得考虑对潜在的错误进行捕捉。而在我们假设的文件管理系统里, 文件的处理也会经常有无可避免的错误。比如我读了一个不存在的文件。
这时我们来观察一下,在它的报错中有这样一个关键词 FileNotFoundError
,这就是我们要处理的异常类型。 那么开始动手捕捉这个错误,并处理它吧。
你运行一下,发现现在已经没有报错了吧,因为潜在的 FileNotFoundError
这个异常已经被 except
给捕捉了起来。 而且捕捉后,我们还进行了一个创建文件并写入文本的工作。当你再点击上面的运行,你会发现重复执行这段代码的话,他就可以正常打印文字到终端了。
处理多个异常¶
如果我的程序在执行某个功能的时候可能会报多种不同的异常,我应该怎么办呢?上面我们处理的都是一种异常,下面我们就用另一种方法来处理多种异常。 首先如果你的多种异常的处理方案是一样的话,我们就能在 except
这里多写几种异常种类。 它会按照我们正常的执行顺序,依次检测异常,报出第一个遇到的异常。
上面这个案例,如果你在原本的 d
中加上一个 gender
, 让 KeyError
不报出来,它就会接着报字典的 IndexError
异常了。
如果我想让两种 error 分开来处理,比如没有 key 的时候,加一个 key,没有 index 的时候加一个 index。能行吗?那就写两个 except
吧。
但是你有没有发现一个小事情,它不会同时处理字典的 KeyError
和列表的 IndexError
,因为在程序顺序执行的时候,只要是报错了, 那么就会终止错误之后的代码,进入错误 回收
环节。这个回收环节在上面的案例中也就是 except
的错误处理环节。 所以你就能发现,其实在你不改动上面代码的情况下,l
列表是没有 append(4)
的。只有当字典正常的时候,列表的报错才会触发。
try-except-else¶
还有一个 try-except-else 的模式,在 else
中处理没有报错的情况,我们对比下面两种写法,一种让它报错,一种不让它报错。 下面的代码,是会报错的代码,我们看到它不会进入到 else
。
下面的代码,我们把 l
加一个位置,就不会报错了,那么代码就会执行到 else
阶段。
try-except-finally¶
你已经见识了大部分的异常处理方法了。如果 else
是为了执行没有异常的状况,那么 finally
就是为了执行 不管有没有异常 的情况。 无论有报错还是没报错,finally
下面的代码都会运行。下面第一段代码是会报错的,第二段不会报错。
上面这两种模式主要用在什么 case 当中呢?你想一下,是不是有些时候,不管你有没有报错,你都想让程序去执行什么。我们甚至都不需要为任何异常做任何处理。 这种时候,也就是说,你有异常吧,我不让你终止主程序,你没有异常吧,万事大吉。
raise手动触发异常¶
有天我被调去了做开源库的工作,为了别人使用到我的代码时不至于骂街,或者异常后报出一大堆很难 debug 的信息。 这个时候我就该考虑考虑 raise 的用法了。 为什么这么说?因为 raise 是你为别人犯错留下的证据,或者是告诉别人你怎么犯错的。这个信息对于别人 dubug 你的代码十分有好处。
另一种情况是,你写了成百上千行代码,你也不能全记住代码的每一个细节。所以一旦报错,你也需要一个友善的错误信息提示,这时用 raise 准没错。
在现在的 IT 互联网时代,我们的程序服务很多都发生在网上,通过网络传输后计算数据。在这种情况下,raise 一个可能发生的错误十分有用。 另外,如果你已经是 Python 玩家了,你在使用其他库的时候肯定遇到过很多报错的情况,这就是别人用上面那种用法告诉你出什么错了。 所以当你为别人提供功能时,你就要学会用 raise。
Python异常错误名称表¶
有哪些能被 raise 的 error 呢?下面我们就列一个表告诉你~
异常名称 | 描述 |
---|---|
BaseException | 所有异常的基类 |
SystemExit | 解释器请求退出 |
KeyboardInterrupt | 用户中断执行(通常是输入^C) |
Exception | 常规错误的基类 |
StopIteration | 迭代器没有更多的值 |
GeneratorExit | 生成器(generator)发生异常来通知退出 |
StandardError | 所有的内建标准异常的基类 |
ArithmeticError | 所有数值计算错误的基类 |
FloatingPointError | 浮点计算错误 |
OverflowError | 数值运算超出最大限制 |
ZeroDivisionError | 除(或取模)零 (所有数据类型) |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
EOFError | 没有内建输入,到达EOF 标记 |
EnvironmentError | 操作系统错误的基类 |
IOError | 输入/输出操作失败 |
OSError | 操作系统错误 |
WindowsError | 系统调用失败 |
ImportError | 导入模块/对象失败 |
LookupError | 无效数据查询的基类 |
IndexError | 序列中没有此索引(index) |
KeyError | 映射中没有这个键 |
MemoryError | 内存溢出错误(对于Python 解释器不是致命的) |
NameError | 未声明/初始化对象 (没有属性) |
UnboundLocalError | 访问未初始化的本地变量 |
ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 |
RuntimeError | 一般的运行时错误 |
NotImplementedError | 尚未实现的方法 |
SyntaxError | Python 语法错误 |
IndentationError | 缩进错误 |
TabError | Tab 和空格混用 |
SystemError | 一般的解释器系统错误 |
TypeError | 对类型无效的操作 |
ValueError | 传入无效的参数 |
UnicodeError | Unicode 相关的错误 |
UnicodeDecodeError | Unicode 解码时的错误 |
UnicodeEncodeError | Unicode 编码时错误 |
UnicodeTranslateError | Unicode 转换时错误 |
Warning | 警告的基类 |
DeprecationWarning | 关于被弃用的特征的警告 |
FutureWarning | 关于构造将来语义会有改变的警告 |
OverflowWarning | 旧的关于自动提升为长整型(long)的警告 |
PendingDeprecationWarning | 关于特性将会被废弃的警告 |
RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 |
SyntaxWarning | 可疑的语法的警告 |
UserWarning | 用户代码生成的警告 |
异常千变万化,告知他人犯了什么错,为别人留下充足的信息,帮助别人找到正确的使用方法吧~