Python 3 错误和异常
作为 Python 初学者,在刚学习 Python 编程时,经常会看到一些报错信息
前面没有提及,是因为这部分内容有点难度,不太适合一入门就学习
Python 中有两种错误很容易辨认:语法错误和异常
语法错误
Python 的语法错误或者称之为解析错,是初学者经常碰到的,就像下面这段代码
>>> while True print('Hello world') File "<stdin>", line 1, in ? while True print('Hello world') ^ SyntaxError: invalid syntax
函数 print() 被检查到有错误,是它前面缺少了一个冒号 ( : )
语法分析器指出了出错的一行,并且在最先找到的错误的位置标记了一个小小的箭头
异常
即便 Python 程序的语法是正确的,在运行它的时候,也有可能发生错误
运行期检测到的错误被称为异常
大多数的异常都不会被程序处理,都以错误信息的形式显示出来
>>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in ? ZeroDivisionError: division by zero >>> 4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in ? NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: Can't convert 'int' object to str implicitly
异常以不同的类型出现,这些类型都作为信息的一部分打印出来: 例子中的类型有 ZeroDivisionError,NameError 和 TypeError
错误信息的前面部分显示了异常发生的上下文,并以调用栈的形式显示具体信息
异常和执行总是被联系在一起。比如打开一个不存在的文件,且没有恰当地处理这种情况,那么程序则被认为是低质量的
如果异常发生,则程序停止。 异常用于处理各种类型的错误,这些错误可能在程序执行期间发生,所以要采取适当的行动,而不至于让程序完全停止
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 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 try: fp = open("demo.txt", "w") fp.write("这是一个测试文件,用于测试异常!!") finally: print ( "Error: 没有找到文件或读取文件失败" )
无论 demo.txt 文件是否有可写权限,都会输出
$ python demo.py Error: 没有找到文件或读取文件失败
如果要写的比较复杂一点,可以如下,不过看起来逻辑更清晰了
#!/usr/bin/python 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 # 定义函数 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 # 定义函数 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 )
预定义的清理行为
一些对象定义了标准的清理行为,无论系统是否成功的使用了它,一旦不需要它了,那么这个标准的清理行为就会执行
这面这个例子展示了尝试打开一个文件,然后把内容打印到屏幕上
for line in open("myfile.txt"): print(line, end="")
以上这段代码的问题是,当执行完毕后,文件会保持打开状态,并没有被关闭
关键词 with 语句就可以保证诸如文件之类的对象在使用完之后一定会正确的执行他的清理方法
with open("myfile.txt") as f: for line in f: print(line, end="")
以上这段代码执行完毕后,就算在处理过程中出问题了,文件 f 总是会关闭