首页 > 代码库 > 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