1.引发异常
在Python中,要想引发异常,最简单的形式就是输入关键字raise,后跟要引发的异常的名称。异常名称标识出具体的类:Python异常是那些类的对象。执行raise语句时,Python会创建指定的异常类的一个对象。raise语句还可指定对异常对象进行初始化的参数。为此,请在异常类的名称后添加一个逗号以及指定的参数(或者由参数构成的一个元组)。
2.异常处理
Python使用try语句实现异常处理。try语句包围着可能引发异常的其他语句。try语句以关键字try开头,后续一个冒号(:)和一个可能在其中引发异常的代码suite。try语句可指定一个或多个except子句,它们紧接在try suite之后。每个except子句都指定了零个或多个异常类名,它们代表要由except子句处理的异常类型。可在except子句中指定一个标志符,程序用它引用被捕捉的异常对象。处理程序利用标识符从异常对象获取也异常有关的信息。如果except子句中没有指定异常类型,就称为"空白except子句"。这种子句会捕捉所有异常类型。在最后一个except子句之后,可选择性地添加一个else子句。如果try suite中的代码没有引发异常,就会执行else子句中的代码。如果try语句没有指定except子句,那就必须包含一个finally子句----该子句肯定会执行,无论是否发生异常。
在try语句中同时包括except和finally子句是语法错误,可接受的组合形式只有:
1 try:
......
except
......
2 try:
......
except
.......
else
......
3 try:
......
finally
........
|
一旦程序代码导致异常,或Python解释器检测到问题,代码或解释器就会“引发”一个异常。有的程序员将程序中发生异常的位置称为“引发点”。异常是一些类的对象,这些类都是从Exception类继承的。如果在一个try suite中发生异常,这个try suite会立即“超时”(即立即终止),程序控制权会移交给try suite之后的第一个except处理程序(如果有的话),接着解释器搜索可对这种异常类型进行处理的第一个except处理程序。解释器为了确定相匹配的except,需要将引发的异常的类型与每个except处理程序的异常类型比较,直到最终发现相匹配的为止。如类型完全一致,或引发的异常的类型是except处理程序的异常类型的一个派生类,就表明发生了匹配。如果try suite中没有发生异常,解释器将忽略用于try语句的异常处理程序,并执行try语句的else子句(如果有的话)。如果没有发生异常,或者某个except子句成功处理了异常,程序会从try语句之后的下一个语句恢复执行。如果在某个语句中发生了一个异常,但该语句不在一个try suite 中,而是一在一个函数中,那么包含那个语句的函数会立即终止,解释器会试图在(发出)调用(的)代码中查找一个封闭的try语句,这个过程称为“堆栈辗转开解”(Stacking Unwinding)。
3.示例:DivideByZeroError
1 # Fig. 12.1: fig12_01.py
2 # Simple exception handling example. 3
4 number1 = raw_input( "Enter numerator: " )
5 number2 = raw_input( "Enter denominator: " )
6
7 # attempt to convert and divide values
8 try: 9 number1 = float( number1 )
10 number2 = float( number2 )
11 result = number1 / number2
12
13 # float raises a ValueError exception
14 except ValueError:
15 print "You must enter two numbers"
16
17 # division by zero raises a ZeroDivisionError exception
18 except ZeroDivisionError:
19 print "Attempted to divide by zero"
20
21 # else clause
22 print "%.3f / %.3f = %.3f" % ( number1, number2, result )
|
float函数将用户输入的值转换成浮点值(第9-10行)。如果不能将字符串参数转换成浮点值,该函数就会引发ValueError异常。如果分母为零,第11行将会引发ZeroDivisionError异常。
每个try suite与其第一个except处理程序之间,两个except处理程序之间,最后一个except处理程序和else子句之间或者try suite和finally子句之间,不允许出现任何语句,否则是语法错误。
尽管可在try suite语句中包含任意语句,但通常只包含可能引发异常的语句,在else suite中,则放置不会引发异常,而且只有在相应的try suite中没有发生异常的前提下才就执行的语句。
except处理程序必须指定要捕捉的异常类名,只有针对默认的“全部捕捉”条件,才就使用一个空白的except处理程序。
4.Python的Exception层次结构
Python 2.4.4 (#2, Apr 5 2007, 20:11:18)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import exceptions
>>> print exceptions.__doc__
Python's standard exception class hierarchy.
Before Python 1.5, the standard exceptions were all simple string objects.
In Python 1.5, the standard exceptions were converted to classes organized
into a relatively flat hierarchy. String-based standard exceptions were
optional, or used as a fallback if some problem occurred while importing
the exception module. With Python 1.6, optional string-based standard
exceptions were removed (along with the -X command line flag).
The class exceptions were implemented in such a way as to be almost
completely backward compatible. Some tricky uses of IOError could
potentially have broken, but by Python 1.6, all of these should have
been fixed. As of Python 1.6, the class-based standard exceptions are
now implemented in C, and are guaranteed to exist in the Python
interpreter.
Here is a rundown of the class hierarchy. The classes found here are
inserted into both the exceptions module and the `built-in' module. It is
recommended that user defined class based exceptions be derived from the
`Exception' class, although this is currently not enforced.
Exception | +-- SystemExit +-- StopIteration +-- StandardError | | | +-- KeyboardInterrupt | +-- ImportError | +-- EnvironmentError | | | | | +-- IOError | | +-- OSError | | | | | +-- WindowsError | | +-- VMSError | | | +-- EOFError | +-- RuntimeError | | | | | +-- NotImplementedError | | | +-- NameError | | | | | +-- UnboundLocalError | | | +-- AttributeError | +-- SyntaxError | | | | | +-- IndentationError | | | | | +-- TabError | | | +-- TypeError | +-- AssertionError | +-- LookupError | | | | | +-- IndexError | | +-- KeyError | | | +-- ArithmeticError | | | | | +-- OverflowError | | +-- ZeroDivisionError | | +-- FloatingPointError | | | +-- ValueError | | | | | +-- UnicodeError | | | | | +-- UnicodeEncodeError | | +-- UnicodeDecodeError | | +-- UnicodeTranslateError | | | +-- ReferenceError | +-- SystemError | +-- MemoryError | +---Warning | +-- UserWarning +-- DeprecationWarning +-- PendingDeprecationWarning +-- SyntaxWarning +-- OverflowWarning +-- RuntimeWarning +-- FutureWarning >>>
|
如图所示,所有异常都从基类Exception继承,而且都在exceptions模块中定义。Python自动将所有异常名称放在内建命名空间中,所以程序不必导入exceptions模块即可使用异常。一旦引发而且没有捕捉SystemExit异常,程序执行就会终止。如果交互式会话遇到一个未被捕捉的SystemExit异常,会话就会终止。Python使用StopIteration异常来判断一个for循环何时抵达序列末尾。Waring异常指定Python的一个特定元素将来可能改变。
异常层次结构的一个优点在于,except处理程序即可捕捉特定类型的异常,也可使用一个基类类型捕捉从它继承的所有类型的异常。
在特定try suite之后,最后一个except之前,设置一个空的except子句属于语法错误。
在特定try suite之后,两个或更多的except子句指定完全相同的异常类型是逻辑错误。Python会执行与引发的异常匹配的第一个except处理程序,忽略其他所有except处理程序。
在其它except处理程序之前,用一个except处理程序捕捉类型为Exception的异常是逻辑错误,因为它会捕捉所有异常,使后面所有异常处理程序形同虚设。
5.finally子句
Python异常处理机制提供了finally子句,只要在程序控制进入相应的try suite,就必然会执行这个子句(无论try suite是成功执行,还是发生异常)。所以,对于在相应try suite中获取和处理的资源,finally suite是放置资源加收代码的理想地点。如果try suite成功执行,finally suite会在try suite终止后立即执行。如果在try suite中发生异常,那么在导致异常的那一行之后,会立即执行finally suite。然后,由下一个封闭的try语句(如果有的话)来处理异常。
finally suite包含的代码一般用于释放相应try块中获得资源,这使finally suite成为消除资源泄露的有效途径。
# Fig. 12.3: fig12_03.py
# Using finally clauses.
def doNotRaiseException():
# try block does not raise any exceptions
try:
print "In doNotRaiseException"
# finally executes because corresponding try executed
finally:
print "Finally executed in doNotRaiseException"
print "End of doNotRaiseException"
def raiseExceptionDoNotCatch():
# raise exception, but do not catch it
try:
print "In raiseExceptionDoNotCatch"
raise Exception
# finally executes because corresponding try executed
finally:
print "Finally executed in raiseExceptionDoNotCatch"
print "Will never reach this point"
# main program
# Case 1: No exceptions occur in called function.
print "Calling doNotRaiseException"
doNotRaiseException()
# Case 2: Exception occurs, but is not handled in called function,
# because no except clauses exist in raiseExceptionDoNotCatch
print "\nCalling raiseExceptionDoNotCatch"
# call raiseExceptionDoNotCatch
try:
raiseExceptionDoNotCatch()
# catch exception from raiseExceptionDoNotCatch
except Exception:
print "Caught exception from raiseExceptionDoNotCatch " + \
"in main program."
|
在finally suite中引发异常是非常危险的。执行finally suite时,假如一个未被捕捉的异常正在等候处理,而finally suite又引发了一个新的,未被该suite捕捉的异常,那么第一个异常就会丢失,新异常则传递给下一个封闭的try语句。
6.Exception对象和跟踪
发生异常时,Python能“记住”引发的异常以及程序的当前状态。Python还维护着traceback(跟踪)
对象,其中含有异常发生时与函数调用堆栈有关的信息。记住,异常可能在一系列嵌套较深的函数调用中引发。程序调用每个函数时,Python会在“函数调用堆栈”的起始处插入函数名。一旦异常被引发,Python会搜索一个相应的异常处理程序。如果当前函数中没有异常处理程序,当前函数会终止执行,Python会搜索当前函数的调用函数,并以此类推,直到发现匹配的异常处理程序,或者Python抵达主程序为止。这一查找合适的异常处理程序的过程就称为“堆栈辗转开解”(Stack Unwinding)。解释器一方面维护着与放置堆栈中的函数有关的信息,另一方面也维护着与已从堆栈中“辗转开解”的函数有关的信息。
# Fig. 12.4: fig12_04.py
# Demonstrating exception arguments and stack unwinding.
import traceback
def function1():
function2()
def function2():
function3()
def function3():
# raise exception, catch exception, reraise exception
try:
raise Exception, "An exception has occurred"
except Exception:
print "Caught exception in function3. Reraising....\n"
raise # reraises most recent exception
# call function1, any Exception it generates will be
# caught by the except clause that follows
try:
function1()
# output exception arguments, string representation of exception,
# and the traceback
except Exception, exception:
print "Exception caught in main program."
print "\nException arguments:", exception.args
print "\nException message:", exception
print "\nTraceback:"
traceback.print_exc()
|
7.程序自定义异常类
程序员通常可用Python层次结构中现有的异常类来标明程序中发生的异常。但在某些情况下,也可新建自己的异常类型,使其与程序中发生的问题严格对应。这种“程序员自定义异常类”就直接或间接继承于Exception类。
# Fig. 12.5: fig12_05.py
# Demonstrating a programmer-defined exception class.
import math
class NegativeNumberError( ArithmeticError ):
"""Attempted improper operation on negative number."""
pass
def squareRoot( number ):
"""Computes square root of number. Raises NegativeNumberError
if number is less than 0."""
if number < 0:
raise NegativeNumberError, \
"Square root of negative number not permitted"
return math.sqrt( number )
while 1:
# get user-entered number and compute square root
try:
userValue = float( raw_input( "\nPlease enter a number: " ) )
print squareRoot( userValue )
# float raises ValueError if input is not numerical
except ValueError:
print "The entered value is not a number"
# squareRoot raises NegativeNumberError if number is negative
except NegativeNumberError, exception:
print exception
# successful execution: terminate while loop
else:
break
|
阅读(5771) | 评论(0) | 转发(1) |