Python 异常处理
异常和执行总是被联系在一起。比如打开一个不存在的文件,且没有恰当地处理这种情况,那么程序则被认为是低质量的
如果异常发生,则程序停止。 异常用于处理各种类型的错误,这些错误可能在程序执行期间发生,所以要采取适当的行动,而不至于让程序完全停止
Python 提供了一个完美的处理异常的机制,可以在 try 语句中附上可能抛出异常的代码,并使用 except 语句告诉 Python 要处理的异常类型
什么是异常?
异常是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行
当 Python 遇到无法处理的程序时,救护引发一个异常
在 Python 中,异常是一个对象,用于表示一个错误
当 Python 脚本发生异常时我们需要捕获和处理它,否则程序会终止执行
异常处理
Python 提供了 try/except 语句用来捕获和处理异常
try 语句用来检测 语句块 中是否有错误
except 语句则用来捕获 try 语句中的异常,并处理
附加的 else 可以在 try 语句没有异常是执行
语法
下面的代码为最简单的 try....except...else 的语法
try: statement(s) # 要检测的语句块 except exception: deal_exception_code # 如果在 try 部份引发了 'exception' 异常 except exception2, e: deal_exception2_code # 如果引发了 'exception2' 异常 else: no_exception_happend_code #如果没有异常发生
try 语句的执行逻辑是: 当开始一个 try 语句后,Python 就在当前程序的上下文中作标记,这样当异常出现时就可以回到这里,try 子句先执行
接下来会发生什么依赖于执行时是否出现异常
- 如果 try 语句发生了发生异常,执行流程就会跳过剩下的 try 语句并执行第一个匹配该异常的 except 语句,异常处理完毕,控制流就通过整个 try 语句(除非在处理异常时又引发新的异常)
- 如果 try 语句发生了异常,却没有匹配的 except 子句,异常将被递交到上层的 try,或者到程序的最上层 ( 这样将结束程序,并打印缺省的出错信息 )
- 如果 try 语句执行时没有发生异常,那么将执行 else 语句后的语句(如果有 else 的话),然后控制流通过整个 try 语句
实例
下面的代码演示了 try/except/else 的简单使用
它打开一个文件,在该文件中的内容写入内容,且并未发生异常
#!/usr/bin/python # -*- coding: UTF-8 -*- try: fp = open("demo.txt", "w") fp.write("这是一个测试文件,用于测试异常!!") except IOError: print "Error: 没有找到文件或读取文件失败" else: print "内容写入文件成功" fp.close()
运行以上 Python 代码,输出结果如下
执行是成功的,因为文件不存在则会新建一个,除非没权限新建文件
$ python main.py
内容写入文件成功
$ cat demo.txt # 查看写入的内容
这是一个测试文件,用于测试异常!!
如果这时候我们将文件 demo.txt 的写权限去掉,命令如下
chmod -w demo.txt
再执行以上代码
$ python demo.py Error: 没有找到文件或读取文件失败
我们可以看到代码引发了一个异常,这个异常是 IOError
类型的,然后被 except
捕获到了
except 语句不带任何异常类型
try: statement(s) # 要检测的语句块 except exception: deal_exception_code # 如果在 try 部份引发了 'exception' 异常 except : deal_all_other_exception2_code # 处理全部其它异常 else: no_exception_happend_code #如果没有异常发生
如果 except 语句而不带任何异常类型,那么它将捕获任何前面 except (如果有的话) 没有捕获的所有异常
这是一种非常好的习惯,因为有时候我们不可能知道所有的可能的异常
except 语句捕获多种异常类型
try: statement(s) # 要检测的语句块 except exception: deal_exception_code # 如果在 try 部份引发了 'exception' 异常 except (Exception1[, Exception2[,...ExceptionN]]]) : deal_all_other_exception2_code # 处理多个异常 else: no_exception_happend_code #如果没有异常发生
我们也可以在一个 except 语句中捕获多个异常类型
只要异常发生时处理逻辑是一样的,那么就可以合并在一起
try - finally 语句
finally 语句用于无论是否发生异常都将执行最后的代码
try: # <语句> finally: # <语句> #退出try时总会执行
注意: 它和 else 语句是不同的, finall 无论是否引发异常都会执行,而 else 语句只有没有异常时才返回执行,也就是说如果没有异常,那么 finally 和 else 语句都会执行
范例
#!/usr/bin/python # -*- coding: UTF-8 -*- try: fp = open("demo.txt", "w") fp.write("这是一个测试文件,用于测试异常!!") finally: print "Error: 没有找到文件或读取文件失败"
无论 demo.txt 文件是否有可写权限,都会输出
$ python demo.py Error: 没有找到文件或读取文件失败
如果要写的比较复杂一点,可以如下,不过看起来逻辑更清晰了
#!/usr/bin/python # -*- coding: UTF-8 -*- try: fp = open("demo.txt", "w") try: fp.write("这是一个测试文件,用于测试异常!!") finally: print "关闭文件" fp.close() except IOError: print "Error: 没有找到文件或读取文件失败"
如果内层的 try 语句抛出一个异常,立即执行 finally 语句
finally 语句中的所有语句执行后,异常被再次触发,并执行外层 except 块代码
注意: 只有 except 语句能捕获异常,finally 语句是不能捕获的
异常的参数
except 语句捕获异常时还可以带上参数,参数保存了异常的详细信息
try: # 正常的操作 ...... except ExceptionType, Argument: # 可以在这输出 Argument 的值.....
Argument 就是异常参数
异常参数是一个元组类型,元组通常包含错误字符串,错误数字,错误位置
范例
下面的代码演示了异常参数的简单应用
#!/usr/bin/python # -*- coding: UTF-8 -*- # 定义函数 def temp_convert(var): try: return int(var) except ValueError, Argument: print "参数没有包含数字\n", Argument # 调用函数 temp_convert("xyz");
运行以上 Python 代码,输出结果如下
$ python main.py 参数没有包含数字 invalid literal for int() with base 10: 'xyz'
触发异常
Python 提供了 raise 语句用于手动引发一个异常
语法
raise [Exception [, args [, traceback]]]
参数说明
参数 | 说明 |
---|---|
Exception | 异常的类型,例如 NameError |
args | 一个异常参数值,该参数是可选的,默认值 "None" |
traceback | 最后一个参数是可选的,用于设置是否跟踪异常对象 |
一个异常可以是一个字符串,类或对象
Python 内置的异常,大多数都是实例化的类,这是一个类的实例的参数
范例
下面的代码演示了如何使用 raise 语句抛出一个异常
def functionName( level ): if level < 1: raise Exception("Invalid level!", level) # 触发异常后,后面的代码就不会再执行
为了能够捕获异常,"except" 语句必须有用相同的异常来抛出类对象或者字符串
如果要捕获上面代码抛出的异常,except 语句应该如下所示
try: # 正常逻辑 except "Invalid level!" # 触发自定义异常 else: # 其余代码
对,没错,就是字符串
范例 2
#!/usr/bin/python # -*- coding: UTF-8 -*- # 定义函数 def mye( level ): if level < 1: raise Exception("Invalid level!", level) # 触发异常后,后面的代码就不会再执行 print('Hello World!') try: mye(0) # 触发异常 except "Invalid level!": print 1 else: print 2
运行以上 Python 代码,输出结果如下
$ python test.py Traceback (most recent call last): File "main.py", line 11, in <module> mye(0) File "main.py", line 7, in mye raise Exception("Invalid level!", level) Exception: ('Invalid level!', 0)
Python 标准异常
下表列出了 Python 内置的异常
异常名称 | 描述 |
---|---|
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 | 用户代码生成的警告 |
用户自定义异常
除了使用 Python 内置的异常,我们还可以创建自己的异常类型
创建自己的异常非常简单,只要创建一个类,并继承 Exception 类或其子类
下面的代码创建了一个异常 Networkerror 继承自 Python 内置的 RuntimeError
用于在异常触发时输出更多的信息
class Networkerror(RuntimeError): def __init__(self, arg): self.args = arg
定义好了之后,我们就可以在 except 语句后使用 Networkerror 异常,,变量 e 是用于创建 Networkerror 类的实例
except Networkerror,e:
然后我们还可以使用 raise 语句手动触发这个异常
raise Networkerror("Bad hostname")
全部代码如下
class Networkerror(RuntimeError): def __init__(self, arg): self.args = arg try: raise Networkerror("Bad hostname") except Networkerror,e: print e.args