异常就是一个Python对象,它表示一个 error。
1、推卸责任:传播异常
当一个函数产生了一个异常,它要么是立刻处理该异常,要么是终止运行。如果这个函数没有处理那个异常,则它的 caller 可能会处理此异常。如果 caller 不处理,则它也会立刻终止运行。异常会顺着调用栈一直往上传播,直到某人处理了它。如果就是没有,则整个程序就终止了。
通常,返回一个值的函数应当返回 None 来指示一个“reasonable” failure,对于“unreasonable” problems 才生成一个异常。
2、处理异常
如果你有一些“受怀疑”的代码(可能会出现异常),此时可以将这些代码放到一个 try: block 中来保护它们。在 try: block 后面,包含一个 except 语句,紧跟着就是用于处理问题的代码。
Listing 5-5: NumberGuess2.py
import random
import sys
# This line chooses a random integer >=1 and <=100.
# (See Chapter 15 for a proper explanation.)
SecretNumber=random.randint(1,100)
print “I’m thinking of a number between 1 and 100.”
# Loop forever (at least until the user hits Ctrl-Break).
while (1):
print “Guess my number.”
# The following line reads a line of input from
# the command line and converts it to an integer.
try:
NumberGuess=int(sys.stdin.readline())
except ValueError:
print “Please type a whole number.”
continue
if (NumberGuess==SecretNumber):
print “Correct! Choosing a new number...”
SecretNumber=random.randint(1,100)
elif (NumberGuess > SecretNumber):
print “Lower.”
else:
print “Higher.”
3、More on exceptions
一个异常可以有一个 argument, 它提供关于问题的额外信息。argument 的内容(以及甚至它的类型)会随着异常而变化。你捕获一个异常的 argument,是通过在 except语句中提供一个变量:
except ExceptionType, ArgumentVariable。
可以提供多个 except 语句来处理各种类型的异常。这种情况下,异常会被首个合适的 except 语句来处理。你还可以提供一个通用的 except 语句,它处理任何异常。如果你这么做了,我高度
建议你在处理异常。因为默默地吞没异常可能会掩盖重要的 bugs,like a NameError。这里是一些 cookie-cutter 代码,我把它用于 quick-and-dirty 错误处理:
try:
DoDangerousStuff()
except:
# The show must go on!
# Print the exception and the stack trace, and continue.
(ErrorType,ErrorValue,ErrorTB)=sys.exc_info()
print sys.exc_info()
traceback.print_exc(ErrorTB)
在 except 语句后面,可以放一个 else 语句,try: block 中的代码如果没有出现异常就会执行 else 块中的代码。else 块中非常适合写那种不需要 try: block 保护的代码。
如果你尝试打开一个不存在的文件,Python 会产生一个异常。
try:
OptionsFile = open("SecretOptions.txt")
except IOError, (ErrorNumber, ErrorString):
# Assume our default option values are all OK.
# We need a statement here, but we have nothing
# to do, so we pass.
pass
else:
# This executes if we opened it without an IOError.
ParseOptionsFile(OptionsFile)
4、Defining and raising exceptions
你可以利用 raise exceptionType, argument 语句来生成异常。Argument 就是 exception argument 的一个值。Argument 是可选的;不过不提供,exception argument 就是 None。
一个异常可以是一个 string、 一个类、或者一个对象。Python core 生成的大多数异常都是 classes,并带一个是该 class的一个实例的 argument。
定义新异常相当简单,下面就是一个:
def CalculateElfHitPoints(Level):
if Level < 1:
raise “Invalid elf level!”,Level
# (The code below won’t execute if we raise
# the exception.)
HitPoints = 0
for DieRoll in range(Level):
HitPoints += random.randint(1,6)
为了捕捉一个异常,except 语句必须指向相同的抛出的异常。Python 通过 reference identity 来比较 string exceptions。因此,如果代码生成“BigProblem”,and a except-clause for "BigProblem,",except语句就不会捕捉该异常。(这些 string 是相等的,但可能没指向内存中的相同点)。要正确地处理异常,应该用一个命名常量字符串,或一个 class。
5、Cleaning up with finally
还有一个处理失败的机制就是 finally 块。你可以提供 except 语句,或一个 finally 语句,不必都提供。
例如,多线程程序常常用锁来阻止线程踩踏各自的数据。如果一个线程得到了一个锁并崩溃了但没有释放这个锁,那么其他线程将永远等待——这就叫死锁。finally 语句就很适合这样的工作:
try:
DataLock.acquire()
# ... do things with the data ...
finally:
# This code *must* execute. The fate of the
# free world hangs in the balance!
DataLock.release()