首页 > 代码库 > Python 点滴 V
Python 点滴 V
【异常语句】
try/except: 捕捉由PYTHON自身或写程序过程中引发的异常并恢复
try/finally: 无论异常是否发生,执行清理行为
raise: 手动在代码中触发异常
assert: 有条件地在程序代码中触发异常
with/as PYTHON后续版本中实现环境管理器
【异常的角色】
下面是它最常见的几种角色
1. 错误处理
>>>可以在程序代码中捕捉和相应错误,或者忽略已发生的异常。
>>>如果忽略错误,PYTHON默认的异常处理行为将启动:停止程序,打印错误信息。
>>>如果不想启动这种默认行为,就用try语句来捕捉异常并从异常中恢复。
2. 事件通知
>>>异常也可用于发出有效状态的信号,而不需在程序间传递结果标志位。或者刻意对其进行测试
3. 特殊情况处理
>>>有时,发生了某种很罕见的情况,很难调整代码区处理。通常会在异常处理中处理,从而省去应对特殊情况的代码
4. 终止行为
>>>try/finally语句可确保一定会进行需要的结束运算,无论程序是否有异常
5. 非常规控制流程
>>>异常是一种高级的goto,它可以作为实现非常规的控制流程的基础。
>>虽然反向跟踪并不是语言本身的一部分,但它能够通过PYTHON异常来实现,需要一些辅助逻辑来退回赋值语句
【新版本自定义类异常例子】
>>> class MyBad(Exception): pass
>>> def doomed(): raise MyBad
>>> try:
... doomed()
... except MyBad:
... print ‘got bad‘
...
got bad
【PYTHON整个异常控制流程】
1. 由PYTHON或程序触发的异常,可以忽略(打印错误信息),也可以由try语句进行扑捉。
2. try语句有两种逻辑格式:
一种是处理异常
另一种是执行最终的代码,而不管异常是否发生.
3. PYTHON的raise,assert可以按照需要触发异常
【try语句格式】
【默认行为例子】
def func(x,y):
return x / y
def test(x):
func(x,0)
test(1)
执行结果:
>>>
Traceback (most recent call last):
File "D:\python\test.py", line 7, in <module>
test(1)
File "D:\python\test.py", line 5, in test
func(x,0)
File "D:\python\test.py", line 2, in func
return x / y
ZeroDivisionError: integer division or modulo by zero
PYTHON没有捕捉的异常会向上传递到PYTHON进程的顶层,并执行PYTHON默认异常处理逻辑,也就是说,PYTHON终止执行中的程序,并打印标准错误信息。
【捕捉内置异常】
def func(x,y):
return x + y #会触发类型异常错误
try:
func([1,2,3],‘abc‘)
except TypeError: #捕捉并恢复
print ‘Hello,World!‘
print ‘resume here!‘ #程序继续执行
执行结果:
>>>
Hello,World!
resume here!
NOTE:
1. 一旦捕捉到异常,控制权会在捕捉的地方继续下去,没有直接的方式回到异常发生的地方.
2. 如果这里没有捕捉到异常,比如说,执行下面的语句
except ZeroDivisionError:
则后面的程序就会终止而不会执行
【try/finally语句例子】
try:
<statements> #首先运行可能触发异常的语句
finally:
<statements> #不管前面是否有异常,finally语句块都会执行
finally语句主要用于:文件关闭,服务器断开等
class MyError(Exception):
pass
def func(file):
raise MyError
f = open(‘data‘) #打开文件
try:
func(f) #手动触发异常
finally:
f.close() #总是会关闭文件
print ‘file closed...‘
【合并try的例子】
下面例子示范了四种常见场景,通过print语句说明其意义
第一种:异常触发并捕获
print '-' * 30,'\nEXCEPTION RAISED AND CAUGHT' try: x = 'spam'[99] except IndexError: print 'except run' finally: print 'finally run' print 'after run'
第二种:无异常触发
print '-' * 30,'\nNO EXCEPTION RAISED' try: x = 'spam'[3] except IndexError: print 'except run' finally: print 'finally run' print 'after run'
第三种:无异常触发,ELSE语句执行
print '-' * 30,'\nNO EXCEPTION RAISED, ELSE RUN' try: x = 'spam'[3] except IndexError: print 'except run' else: print 'else run' finally: print 'finally run' print 'after run'
第四种:异常触发,但没有捕获
print '-' * 30,'\nEXCEPTION RAISED BUT NOT CAUGHT' try: x = 1 / 0 except IndexError: print 'except run' finally: print 'finally run' print 'after run'执行结果:
>>> ------------------------------ EXCEPTION RAISED AND CAUGHT except run finally run after run ------------------------------ NO EXCEPTION RAISED finally run after run ------------------------------ NO EXCEPTION RAISED, ELSE RUN else run finally run after run ------------------------------ EXCEPTION RAISED BUT NOT CAUGHT finally run Traceback (most recent call last): File "D:\python\test.py", line 34, in <module> x = 1 / 0 ZeroDivisionError: integer division or modulo by zero【raise语句】
要故意触发异常,请使用raise. 形式简单,使用方便.
格式一: raise <name> #手工触发一个异常
格式二: raise <name>, <data> #触发异常并传递额外的数据到捕获器
格式三: raise #重新触发,如果想把刚捕获的异常传递给另外一个处理器,必须用到
【raise简单例子】
class MyError: #定义MyError类,注意字符串不能用于捕获
print ‘My Error class‘
def func():
raise MyError #手工触发自定义异常类
try:
func() #尝试捕获异常
except MyError: #匹配/查找,触发的异常是否一致
print ‘got myError‘ #如match,则处理异常
【利用raise传递额外的数据】
raise语句可以随异常一起传递额外的数据项给处理器使用。一般而言,额外数据可以向处理器传递关于异常的背景信息。例如:如果正在写数据文件分析器,可能会在遇到错误时引发语法错误异常,此外也传入一个对象,把行号和文件信息提供给处理器。
这是很有用的,因为异常引发时,也许会跨越任意的文件边界:触发异常的raise语句以及进行捕捉的try语句,也许位于完全不同的文件中,一般来说:无法把额外的细节存储在广域变量内,因为try语句可能不知道广域变量位于哪个文件中。和异常本身一起吧额外的数据传递进去,可让try语句读取时,更有把握。 就像函数的返回值:
每个异常都有这个额外的数据,如果没有刻意的传递什么时,就默认为特殊的None对象.
class MyException(Exception): pass def raiser1(): raise MyException, 'Pass Extra Data to Raiser.' def raiser2(): raise MyException def tryer(func): try: func() except MyException, extrainfo: print 'got this', extrainfo tryer(raiser1) tryer(raiser2)运行结果:
>>>
got this Pass Extra Data to Raiser.
got this
【利用raise传递异常】
raise语句不包括异常名称或额外值时,就是重新引发当前异常。如果需要捕捉和处理一个异常,又不希望异常在程序代码中死掉,一般就会使用这种方式.
>>> try: ... raise IndexError, 'spam' ... except IndexError: ... print 'raise pass exception to top cather!' ... raise ... raise pass exception to top cather! Traceback (most recent call last): File "<stdin>", line 2, in <module> IndexError: spam通过这种方式执行raise时,会重新引发异常,并将其传递给更高层的处理器,或者顶层默认的处理器,也就是停止程序,打印标准错误消息。
【assert语句】
这是raise常见使用模式的语法简写,可视为带条件的raise语句。语句格式为:
assert <test>, <data> #<data>部分可选
比如下面例子:
如果断言成立的话,不会触发异常
>>> myStr = ‘spam‘
>>> assert len(myStr) > 1, ‘The length of string is more than 1‘
>>> assert range(4)==[0,1,2,3]
>>> assert 1 == 1
如果断言不成立的话,则触发AssertionError的异常
>>> assert len(myStr) > 10, ‘The length of string is more than 1‘
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: The length of string is more than 1
所以其语法等价于:
if __debug__:
if not <test>:
raise AssertionError,<data>
【assert作用】
assert主要是收集约束条件,而不是错误或异常!assert语句是用于验证开发期间程序状况的。
显示时,其错误信息正文会自动包括源代码的行信息。以及列在assert语句中的值。如下例
#File: asserter.py
def func(x):
assert x < 0, ‘x must be negative!‘
return x ** 2
执行结果:
>>> from asserter import *
>>> func(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "asserter.py", line 2, in func
assert x < 0, ‘x must be negative!‘
AssertionError: x must be negative!
牢记一点:assert都是用来收集用户定义的约束条件的,而不是捕捉内在的程序设计错误的。因为PYTHON会自动收集程序的设计错误,通常来说是没有必要写assert去捕捉超出索引值,类型不匹配以及除数为0之类的事情。比如说下面:
def func(x,y):
assert x !=0 #这是没有必要的,PYTHON会自动处理
return x / y #被除数为0的情况.
【with/as环境管理器】
从PYTHON2.6开始,引入的新的异常相关的语句: with以及可选的as分句。这个语句的设计是为了和环境管理器对象一起工作。
简而言之:with/as语句的设计是作为常见try/finally用法模式的替代方案。 就像try/finally语句,with/as语句也是用于定义必须执行的终止或清理行为,无论处理步骤中是否发生异常。和try/finally不同的是,with语句支持更丰富的基于对象的协议,可以为代码块定义支持进入或离开的动作。
PYTHON以环境管理器强化一些内置工具,例如,自动关闭打开的文件,以及对锁的自动上锁和开锁。程序员也可以用类编写自己的环境管理器。
【with/as用法】
因with/as是PYTHON2.6才开始使用的,所以2.5及之前的版本需要导入:
>>> from __future__ import with_statement
语句格式如下:
with experssion [as variable]: #as为可选
with-block
这里expression要返回一个对象,从而支持环境管理协议
如果选用as分句时,此对象也可返回一个值,将其赋值给变量名variable
NOTE:
1. variable并非赋值为experssion的结果。
2. expression的结果是支持环境协议的对象,而variable则是赋值为其他的东西
3. experssion返回的对象可在with-block开始前,先执行启动程序,并且在该代码完成后,执行终止程序代码,无论该代码块是否引发异常。
with open(r‘data.txt‘) as myfile:
for line in myfile:
print line
或者其他支持环境管理的协议:
lock = threading.Lock()
with lock:
with-block
【PYTHON中常用异常处理测试模块】
def doStuff():
doFirstthing()
doSecondthing()
doLastthing()
def goodEnd(): pass
def badEnding(): pass
if __name__== ‘__main__‘:
try:
doStuff()
except:
badEnding()
else:
goodEnding()
finally:
file.close()
conn.close()
【PYTHON异常语句】
简单来说:
try: 是捕捉
raise: 是触发
assert: 是条件式引发
with: 是把代码块包装在环境管理器中,而从定义进入和离开的行为
【with/as实际例子】
【类异常特点】
和旧的字符串异常模型相比,类异常有如下特点
. 提供类型分类,对今后的修改有更好的支持:新增异常时,不需要在try语句中进行修改
. 提供了存储在try处理器中所使用的环境信息的合理地点:这样的话,可以拥有状态信息以及可调用的方法,并且可以通过实例进行读取
. 允许异常参与继承层次,从而获得共同的行为。例如:继承的显示方式可提供通用的错误信息的外观。
基于类的异常支持了程序的演进和较大系统,从这方面讲,它远优于基于字符串的异常.
【字符串异常】
PYTHON2.5之前的版本,还可以
>>> myexc = "My exception string"
>>> try:
... raise myexc
... except myexc:
... print ‘caught‘
caught
之后的版本,显示异常:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: exceptions must be old-style classes or derived from BaseException, not str
【基于类的异常】
字符串和类异常的主要区别在于,引发的异常在try语句中的except分句匹配时的方式不同。
. 前者是以简单对象识别来匹配的:引发的异常是由PYTHON的is测试来匹配except分句的
. 后者是由超类关系进行匹配:只要except分句列举了异常的类,或其任何超类名,引发的异常就会匹配该分句
当try语句的except分句列出一个超类时,就可以捕捉该超类的实例,以及类树中所有较低位置的子类的实例。结果就是类异常支持异常的层次架构:超类变成分类的名称,而子类变成了这个分类中的异常。except分句列出一个通用的异常超类,就可以捕捉整个分类中的各种异常:任何特定的子类都可匹配。
【类异常例子】
#File: classexc.py
class Super: pass class Sub01(Super): pass class Sub02(Super): pass def raiser0(): X = Super() raise X def raiser1(): X = Sub01() raise X def raiser2(): X = Sub02() raise X for func in (raiser0,raiser1,raiser2): try: func() except Super: #匹配父类及子类 import sys print 'caught:',sys.exc_info()[0]执行结果:
C:\>python classexc.py
>>>
caught: __main__.Super
caught: __main__.Sub01
caught: __main__.Sub02
NOTE:
1. sys.exc_info调用,这是一种抓取最近发生异常的常见方式
2. 引发基于类的异常,一定要有个实例
3. PYTHON官方文档之处,自定义类最后继承自Exception
class Super(Exception): pass
【内置Exception类】
1. 所有熟悉的异常(如:IndexError,SyntaxError)其实都是预定义的类,可以作为内置变量名,放在__builtin__模块中.
2. 以及作为标准库模块exceptions的属性。
3. PYTHON把内置异常组织成层次,来支持各种捕捉模式
Exception: 异常的顶层根超类
|
StandardError: 所有内置错误异常的超类
|
ArithmeticError: 所有数值错误的超类
|
OverflowError: 识别特定的数值错误的子类
查询办法:
>>> import exceptions
>>> help(exceptions)
>>> dir(exceptions)
【定义异常文本】
对于未被捕获的类异常,消息包含什么呢? 在默认的情况下,得到的是类的名称以及被抛出的实例对象。
>>> class MyBad: pass
...
>>> raise MyBad()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
__main__.MyBad: <__main__.MyBad instance at 0x000000000285A608>
上述显示不太美观,作为改进,可以用__repr__,__str__重载函数方法,来重写自己想要的默认异常结果:
>>> class MyBad:
... def __repr__(self):
... print ‘Sorry,My mistake!‘
...
>>> raise MyBad()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
__main__.MyBadSorry,My mistake!
NOTE:另外一种情况,如果我们的类继承自内置异常类,错误显示会发生微妙的改变,因为构造方法参数会自动存储并显示在消息中,如下:
>>> class MyBad(Exception): pass
...
>>> raise MyBad()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
__main__.MyBad
>>> class MyBad(Exception): pass
...
>>> raise MyBad(‘This‘,‘is‘,‘super‘,‘exception‘,‘test!‘)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
__main__.MyBad: (‘This‘, ‘is‘, ‘super‘, ‘exception‘, ‘test!‘)
【类的额外数据传递】
分析数据文件的程序可能引发异常实例(填入有关错误的额外细节)从而发出格式错误的信号
class FormatError: #格式化错误 def __init__(self,line,file): self.line = line self.file = file def parser(): #When error found raise FormatError(42,file='spam.txt') try: parser() except FormatError, X: print 'Error at', X.line,X.file在except分句中,变量X赋值为异常引发时所产生的实例。上述的传递异常的额外数据,用字符串的方式也可以实现,但是如果类有行为时,类这种方法可能更方便些
class FormatError: def __init__(self,line,file): self.line = line self.file = file def logerror(self): log = open('formaterror.txt','a') print >> log, 'Error at', self.file, self.line def parser(): raise FormatError(40,'spam.txt') try: parser() except FormatError, exc: exc.logerror()【raise语句的一般格式】
raise语句有5种形式,前两者是引发字符串异常,后两个是引发类异常,而最后一个是重新引发当前异常
raise string #匹配对象为string的异常
raise string,data #在上面的基础上再传递额外的数据
raise instance #如: raise instance.__class__.instance
raise class,instance #匹配该异常类及她的超类
raise #重新触发异常,如果需要传递任意的异常时,就有用处
比如说,要触发KeyError的异常可以用以下几种方式之一:
raise KeyError() #正常格式: 触发一个实例
raise KeyError,KeyError #类,实例: 使用实例
raise KeyError #类:一个实例将要产生
raise KeyError,‘spam‘ #类,变量:一个实例将要产生
而下面的raise,则是将X赋值给了引发的KeyError实例对象
try: pass
except KeyError, X: pass
【PYTHON将不再使用基于字符串的异常】
Guido说:在未来PYTHON版本会消失。这样做的理由:
基于字符串的异常不支持分类、状态信息或行为继承,不像基于类的异常。在程序规模比较小时,还比较容易使用,一旦程序规模变大,就变得难以使用了。
【嵌套异常处理器】
try/except语句:
try/finally语句:
【try/except嵌套例子】
#File: exec.py def action1(): print 1 + [] #通常的类型错误 def action2(): try: action1() except TypeError: #最近的找到的匹配错误 print 'inner error' try: action2() except TypeError: print 'outer error'执行结果:
C:\>python exec.py
inner error
这里有两个激活的try语句,一个在action1内,一个在action2内。PYTHON会挑选并执行except最近的try,本例子中就是action1()中的try.
【语法嵌套化例子】
上面的代码给下面的等价
def action1(): print 1 + [] try: try: action1() except TypeError: print 'inner try' except TypeError: print 'outer try'如果换成为try/finally,代码如下:
try: try: raise TypeError finally: print 'inner try' finally: print 'outer try'执行结果:
>>> inner try outer try Traceback (most recent call last): File "D:\python\test.py", line 3, in <module> raise TypeError TypeError
下面是try/except/finally嵌套实现的代码及业务逻辑
def raiser1(): return def raiser2(): raise EOFError def raiser3(): raise TypeError for func in (raiser1,raiser2,raiser3): print '\n',func try: try: func() except TypeError: print 'caught TypeError!' finally: print 'finally run'执行结果如下:
>>>
<function raiser1 at 0x000000000256E748>
finally run
<function raiser2 at 0x0000000002DDB0B8>
finally run
Traceback (most recent call last):
File "D:\python\test.py", line 8, in <module>
func()
File "D:\python\test.py", line 2, in raiser2
def raiser2(): raise EOFError
EOFError
【异常不总是错误】
在PYTHON中错误都是异常,但异常不总是错误。比如对文件对象进行读取操作,文件末尾会有空字符,sys.stdin读取末尾的空字符串行会报EOFError的异常,这不是错误,是正常行为。除非放到try,并放到嵌套的循环内,如:
while 1:
try:
line = raw_input(‘Enter here:‘)
except EOFError:
break
else:
...
【函数信号条件和raise】
用户定义的异常也可引发非错误的情况。比如:搜索程序可以写成找到相符者引发异常,而不是为调用者返回状态标志来拦截。在下面的代码中,try/except/else处理器做的就是if/else返回值的测试工作
class Found(Exception): pass def searcher(): if ...success...: raise Found() else: return try: searcher() except Found: ...success... else: ...failure...如果所有对象都是可能的有效返回值,就不可能以任何返回值来代表不寻常的情况。异常则提供了一种方式来传达结果讯息,而不使用返回值,如下例
class Failure(Exception): pass def searcher(): if ...success... return ...founditem... else: raise Failure() try: item = searcher() except Failure: ...report... else: ...use item here...
因为PYTHON核心是动态类型和多态的,通常更趋向于使用异常来发出这类情况的信号,而不是警示性的返回值。
【】【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
【】
Python 点滴 V