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分句列出一个通用的异常超类,就可以捕捉整个分类中的各种异常:任何特定的子类都可匹配。

【类异常例子】

例1: 

class MyException:
    def __init__(self,value):
        self.value = value
    def __str__(self):
        return 'self.value'

try:
    raise MyException, 'My bad!'
except MyException, e:
    print "My Exception occured, value:", e.value
#输出结果
>>>
>>> 
My Exception occured, value: My bad!


例2:

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核心是动态类型和多态的,通常更趋向于使用异常来发出这类情况的信号,而不是警示性的返回值。

【在try外进行调试】

可以利用异常处理器,取代PYTHON的默认顶层异常处理行为。在顶层代码中的外层try中包装整个程序,就可以捕捉任何程序执行时会发生的异常,因此可替代默认的程序终止行为。空的except分句会捕捉任意程序执行时所引发的而未被捕捉到的异常。要取得所发生的实际异常,可以从内置sys模块取出sys.exc_info函数返回的结果:

try:
    ...run program...
except:
    import sys
    print 'uncaught|',sys.exc_info()[0],sys.exc_info()[1]

这种结构在开发期间会经常使用,在错误发生时,仍保持程序处于激活状态:这样可以执行其他的测试,而不用重新开始。测试其他程序时,也会用到她。

【运行进程中的测试】
import sys
log = open('testlog','a')
from testapi import moreTests,runNextTest,testName

def testdrive():
    while moreTests():
        try:
            runNextTest()
		except:
		    print >> log,'FAILED',testName(),sys.exc_info()[:2]
		else:
		    print >> log,'PASSED',testName()
			
testdriver()

在这个例子中,模块testapi是抽象的。

在这里的testdriver函数会循环进行一组测试调用。因为测试案例中未被捕捉的异常,一般都会终止这个测试驱动程序,如果我们想在测试失败后让测试进程继续下去,就需要try中包装测试案例的调用。

空的except会捕捉由测试案例所产生的没有被捕获的异常,而其使用sys.exc_info把该异常记录到文件中。测试成功时,else分句就会执行。对于作为测试驱动运行在同一个进程的函数,模块,类,而进行测试的系统而言,这种形式固定的代码是很典型的。

更完整的测试框架:Doctest,PyUnit

sys.exc_info

sys.exc_info结果是获得最近引发的异常的最好方式。如果没有处理器正在处理,就返回包含三个None值的元祖

>>> import sys
>>> print sys.exc_info()
(None, None, None)
否则会返回(type,value和traceback)
log = open('log.txt','a')
try:
    print 1 / 0
except ZeroDivisionError:
    import sys
    print >> log, sys.exc_info()
    
执行结果:
(<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('integer division or modulo by zero',), <traceback object at 0x0000000002DE12C8>)
. type是正在处理的异常的异常类型

. value是异常参数(她的关联或raise的第二参数,如果异常类型为类对象,就一定是类实例)

. traceback是一个traceback对象,代表异常最初发生时所调用的堆栈
【与异常相关技巧:应该包装什么】

这其实是设计的问题,不在语言范围本身,而且实际运用时,就会更明显,列出一些简要的原则:

. 经常会失败的运算一般都应该包装在try语句中

  >>>例如:和系统状态衔接的运算(文件开启、套接字调用等)就是try的主要候选者

. 简单的脚本,如希望这类运算失败时终止程序,而不是被捕捉或被忽略,就用放try里面

. 应该在try/finally中实现终止动作,从而包装它们的执行。无论异常是否发生,如文件句柄,数据库连接关闭

. 偶尔,把对大型函数的调用包装在单个try语句中,而不是让函数本身零散放在若干的try语句中,这样更方便

【捕捉太多:避免空except】

空except分句会捕捉try代码块中代码执行时所引发的每一个异常。写很容易,有时候也能得到我们想要的结果,但这样做的话可能会拦截到高层的try处理器所期待的时间。如下例子中异常处理器,会捕捉到每个到达的异常并使其停止,无论是否有另外一个处理在等待该事件.

def func():
    try:
        raise IndexError
    except:
        print 'inner try'  #任何异常都会被捕获

try:
    func()
except IndexError:         #实际需要在此捕捉的异常
    print 'outer try'
    
这类代码可能会捕捉无关的系统异常。例如:内存错误、一般程序错误、迭代停止以及系统退出等,都会在PYTHON中引发异常。这类异常通常是不应该被拦截的。例如:sys.exit(statuscode)调用提前终止,这实际上是引发内置的SystemExit异常来终止程序,使try/finally可以离开前执行,而程序的特殊类型可拦截该事件,因此,try带except时,可能会不知不觉阻止重要的结束。
import sys
def func():
    sys.exit(40)

try:
    func()
except:
    print 'got it'
print 'continue...'
运行结果:
>>> 
got it
continue...
更坏的情况是: 空except会捕捉一般程序设计错误,但这类错误多数情况下应该让其通过
dictionary = {'a':1,'b':2}

try:
    x = ditctionary['a']     #拼写错误的情况
except:
    x = None                 #建设我们获得了KeyError
字典名拼写错了,但用这种方式捕捉,PYTHON会为未定义的变量名引发NameError,但处理器会默默的捕捉并忽略这个异常。如果这件事发生在离读取值的使用很远的地方,就会变成一项很难的调试任务!

所以原则就是:尽量让处理器具体化,空except分句很方便,但是很容易出错。例如上面的例子,最好写成except KeyError,意图明确,避免拦截无关的事件.
【捕捉过少:使用基于类的分类】

另一方面,捕获器也不应该太过具体化,比如下面:

下面的处理器是把myerror1和myerror2看作是正常情况,并把其他的一切视为错误。如果未来增加了myerror3,就会视为错误,除非更新异常列表

try:
    ...
except myerror1,myerror2:
    ...
很好的替代方案是:将所有的异常放置在一个超类中
class super(Exception): pass
class myerror1(super):  pass
class myerror2(super):  pass
class myerror3(super):  pass
try:
    ...
except super:
    ...
到时候有更新的话,只需要加一句: class myerror3(super): pass就可以了。

基于异常类的层次分类,可以让代码更好管理与维护,特别是大型系统。异常规则也应该是整体设计的一部分。
【字符串异常匹配是通过同一性而不是通过值】

字符串比较短的情况

>>> ex1 = 'hello'
>>> ex2 = 'hello'
>>> ex1 == ex2, ex1 is ex2
(True, True)

字符串比较长的情况

>>> ex1 = 'This is a long sentence!'
>>> ex2 = 'This is a long sentence!'
>>> ex1 == ex2, ex1 is ex2
(True, False)

NOTE;字符串足够长的话,会突破PYTHON的字符串的内部缓存机制,变成一个全新的对象。

因此,如果是同一对象的话,字符串捕捉的异常会和except匹配

>>>try:

    raise ex1

except ex1:

    print 'got it'

got it

如果不是同一对象的话,下面的则会失败

try:

    raise ex1

except ex2:

    print 'got it'

在比如,下面是成功的

try:

    raise 'spam'

except 'spam':

    print 'got it'

而这样确实失败的

try:

    raise 'spam'*8

except 'spam'*8

    print 'got it'

好在:新的PYTHON的版本会逐渐淘汰字符串异常格式

【PYTHON工具集】

一般而言,PYTHON提供了一个有层次的工具集

内置工具:

    像字符串、列表以及字典这些内置类型,会让编程更迅速

PYTHON扩展

    就更重要的任务来说,可以编写自己的函数、模块以及类,来扩展PYTHON

已编译的扩展

    PYTHON也可以使用C/C++这样的外部语言编写的模块进行扩展




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值