异常机制是衡量一个编程语言是否成熟的标准之一,使用异常处理机制的python程序容错性会更好。
在自动化脚本开发程中,往往会出现一些不可预知的错误,这种情况在编程语言中叫做异常。遇到异常,我们要处理它,不是等到异常发生的时候,让IDE帮你处理,等他给你抛出异常的时候,就晚了。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。
因此需要我们在可能发生异常的地方,主动使用try/except语句。来检测try语句块中的错误,从而让except语句捕获异常信息并处理。
python的异常机制主要依赖try、except、else、finally和raise五个关键字,其中在try关键字后缩进的代码块简称try块,它里面放置的是可能引发异常的代码
在except之后对应的是异常类型和一个代码块,用于表明该except块处理这种类型的代码块;在多个except块之后可以放一个else块,表明程序不出现异常时还要继续执行else块;最后还可以跟一个finally块,finally块用于回收在try块里打开的物理资源,异常机制会保证finally块总被执行;而raise用于引发一个实际的异常,raise可以单独作为一个语句使用,引发一个具体的异常对象。
内置异常类的层次结构如下:
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 # 与资源使用相关的警告的基类。被默认警告过滤器忽略。
python的异常处理机制可以让程序具有极其好的容错性。当程序运行出现意外情况时,系统会自动生成一个error对象来通知程序
语法结构:
try:
#业务实现代码
...
except (Error1,Error2,...) as e:
alert 输入不合适
goto retry
如果在执行try块里的业务逻辑代码时出现异常,系统自动生成一个异常对象,该异常对象被提交给python解释器,这个过程被称为引发异常。
当Python解释器收到异常对象时,会寻找能处理异常对象的except块,如果找到合适的except块,则把该异常交给该except块处理,这个过程被称为捕获异常。
如果python解释器找不到捕获异常的except块,则运行时环境终止,python解释器也将退出
不管程序代码块是否处于try块中,甚至包括except块中的代码,只要执行该代码块时出现异常,系统总会自动生成一个Error对象。如果程序没有为这段代码定义任何的except块,则python解释器无法找到处理该异常的except块,程序就在此退出,并抛出错误
try 语句按照如下方式工作:
首先,执行 try 子句(在关键字 try 和关键字 except 之间的语句)。
如果没有异常发生,忽略 except 子句,try 子句执行后结束。
如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行。
如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。
python中try块后面可以有多个except块,目的是为了针对不同的异常类提供不同的异常处理方式。当程序发生不同的意外情况时,系统会生成不同的异常对象,python解释器就会根据该异常对象所属的异常类来决定使用哪个except块来处理该异常
实例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys
try:
a = int(sys.argv[1])
b = int(sys.argv[2])
c = a / b
print("您输入的两个数相除的结果是:",c)
except IndexError:
print("索引错误,运行程序时输入的参数个数不够")
except ValueError:
print("数值错误:程序只能接收整数参数")
except ArithmeticError:
print("算术错误")
except Excepction:
print("未知错误")
实例中导入了sys模块,并通过sys模块的argv列表来获取运行python程序时提供的参数,其中sys.argv[0]通常代表正在运行的python程序名,sys.argv[1]代表运行程序的所提供的第一个参数,sys.argv[2]代表运行程序的所提供的第二个参数,。。。以此类推。
当捕获多个异常时,可以把要捕获的异常的名字,放到except 后,并使用元组的方式仅进行存储
在使用一个except块捕获多种类型的异常时,只要将多个异常类用圆括号扩起来,中间用逗号隔开即可(也就是构建多个异常类的元组)
实例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys
try:
a = int(sys.argv[1])
b = int(sys.argv[2])
c = a / b
print("您输入的两个数相除的结果是:",c)
Except (IndexError,ValueError,ArithmeticError):
print("程序发生了数组越界、数字格式异常、算术异常之一")
except Excepction:
print("未知错误")
Except (IndexError,ValueError,ArithmeticError)这里来指定了所捕获的异常类型,也就是表明了这个except可以同时捕获三种类型的异常
如果程序需要在except块中访问异常对象的相关信息,则可以通过为异常对象声明变量来实现。当python解释器决定调用某个except块来处理该异常对象时,会将异常对象赋值给except块后面的异常变量,程序就可以通过这个变量来获得异常对象的相关信息。
所有的异常对象都包含了以下几个常用的属性和方法:
1、args:该属性返回异常的错误编号和描述字符串
2、errno:该属性返回异常的错误编号
3、strerror:该属性返回异常的描述字符串
4、with_traceback(): 通过该方法可以处理异常的传播轨迹信息
实例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
def foo():
try:
fis = open("a.txt");
except Exception as e:
#访问异常的错误编号和详细信息
print(e.args)
#访问异常的错误编号
print(e.errno)
#访问异常的详情信息
print(e.strerror)
foo()
运行结果:
(2, 'No such file or directory')
2
No such file or directory
如果要访问异常对象,只要在单个异常类或者异常元组(多异常捕获)之后使用as再加上异常扮靓即可
解析:
该程序调用了Exception对象的args属性(该属性其实就相当于同时返回了errno和strerror这两个属性)访问异常的错误编号和详情信息
在python异常处理流程中还可以添加一个else块,当try块没有出现异常时,程序会执行else块。
try/except 语句还有一个可选的 else 子句,如果使用这个子句,那么必须放在所有的 except 子句之后。
else 子句将在 try 子句没有发生任何异常的时候执行。
try…except语法声明格式:
try:
<语句> #运行别的代码
except <名字>:
<语句> #如果在try部份引发了'name'异常
except <名字>,<数据>:
<语句> #如果引发了'name'异常,获得附加的数据
else:
<语句> #如果没有异常发生
实例1:
打开一个文件,在该文件中的内容写入内容,且并未发生异常
#!/usr/bin/python
# -*- coding: UTF-8 -*-
try:
fh = open("testfile", "w")
fh.write("这是一个测试文件,用于测试异常!!")
except IOError:
print "Error: 没有找到文件或读取文件失败"
else:
print "内容写入文件成功"
fh.close()
结果:
$ python test.py
内容写入文件成功
$ cat testfile # 查看写入的内容
这是一个测试文件,用于测试异常!!
实例2:
打开一个文件,在该文件中的内容写入内容,但文件没有写入权限,发生了异常
#!/usr/bin/python
# -*- coding: UTF-8 -*-
try:
fh = open("testfile", "w")
fh.write("这是一个测试文件,用于测试异常!!")
except IOError:
print "Error: 没有找到文件或读取文件失败"
else:
print "内容写入文件成功"
fh.close()
结果:
$ python test.py
Error: 没有找到文件或读取文件失败
使用 else 子句比把所有的语句都放在 try 子句里面要好,这样可以避免一些意想不到,而 except 又无法捕获的异常。
异常处理并不仅仅处理那些直接发生在 try 子句中的异常,而且还能处理子句中调用的函数(甚至间接调用的函数)里抛出的异常
实例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
s = input('请输入除数:')
try:
result = 20 / int(s)
print('20除以%s的结果是:%g' %(s,result))
except ValueError:
print('值错误,您必须输入数值')
except ArithmeticError:
print('算术错误,您不能输入0')
else:
print('没有出现异常')
运行结果:
请输入除数:a
值错误,您必须输入数值
请输入除数:0
算术错误,您不能输入0
请输入除数:5
20除以5的结果是:4
没有出现异常
有时候在try块里打开一些物力资源,如数据库连接、网络连接和磁盘文件等,这些资源都必须被显示回收
说到资源回收可能会联想到Python的垃圾回收机制,其实还是不一样的,Python的垃圾回收机制不会回收任何物理资源,只能回收堆内存中对象所占用的内存
为了保证一定能回收在try块里打开的物理资源,异常处理机制提供了finally块,不管try块中的代码是否出现异常,也不管哪一个except块被执行,甚至在try块或者except块中被执行了return语句,finally块也总会被执行
即:try-finally 语句无论是否发生异常都将执行最后的代码
语法如下:
try:
#业务实现代码
...
except SubException as e:
#异常处理模块1
...
except SubException2 as e:
#异常处理模块2
...
...
else:
#正常处理块
...
finally:
#资源回收块
...
python总会执行finally子句,无论try子句执行时是否发一异常。
如果没有发生异常,python运行try子句,然后是finally子句,然后继续。
如果在try子句发生了异常,python就会回来执行finally子句,然后把异常递交给上层try,控制流不会通过整个try语句。当你想无论是否发生异常都确保执行某些代码时,try/finally是有用的。
实例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
try:
msg = input(">>")
print(int(msg))
except Exception as e:
print("异常的类型是:%s"%type(e))
print("异常的内容是:%s"%e)
else:
print('如果代码块不抛出异常会执行此行代码!')
finally:
print('不管代码块是否抛出异常都会执行此行代码!')
运行结果1:
1
如果代码块不抛出异常会执行此行代码!
不管代码块是否抛出异常都会执行此行代码!
运行结果2:
2
如果代码块不抛出异常会执行此行代码!
不管代码块是否抛出异常都会执行此行代码!
在异常处理语法结构中,只有try块是必需的,也就是说,如果没有try块则不能有后面的except块和finally块,except块和finally块都是可选的,但except块和finally块至少出现其中的一个,也可以同时出现,可以有多个except块,但捕获父类异常的except块应该位于捕获子类异常的except块之后,不能只有try块,既没有except块,也没有finally快。多个except块必须位于try块后面,finally块必须位于所有的except块的后面
实例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os
def test():
fis = None
try:
fis = open("a.txt")
except OSError as e:
print(e.strerror)
#return语句强制方法返回
return
#os.__exit(1)
finally:
#关闭磁盘文件,回收资源
if fis is not None:
try:
#关闭资源
fis.close()
Except OSError as ioe:
print(ioe.strerror)
print("执行finally块里的资源回收!")
test()
在通常情况下,一旦在方法里执行到return语句,程序将立即结束该方法,现在不会了,虽然return语句也强制方法结束,但一定会先执行finally块的代码
除非在try块、except块中调用了退出python解释器的方法,否则不管在try块还是在except块中执行代码,或者执行怎么样的代码,出现怎么样的情况,异常处理的finally块总会被执行。调用sys.exit()方法退出程序不能阻止finally块的执行,这是因为sys.exit()方法本身就是通过引发SystemExit异常来退出程序的。
不要在finally块中使用return或者raise(后面会讲到)等导致中止的语句,一旦在finally块中使用了return或者raise语句,将会导致try块、except块中的return、raise语句失效
实例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
def test():
try:
#因为finally块中包含了return语句
#所以下面的return语句失去作用
return True
finally:
return False
a = test()
print(a)
尽量避免在finally块中使用return或者raise等导致方法终止的语句,否则则可能会出现一些奇奇怪怪的错误(有兴趣的可以发掘一下子!)
更新中。。。