本章讨论python中的异常处理机制,包括熟悉python中异常处理的写法,如果用raise出发异常,如何通过异常堆栈文本定位和了解异常情况。

异常

错误与异常

错误:从软件方面来说,错误是语法或逻辑上的。语法错误指示软件的结构有误,导致不能被解释器解释或编译器无法编译,这些错误必须在程序执行前纠正。逻辑错误可能是由于不完整或者不合法的输入所致。
当python检测到一个错误时,解释器就会指出当前流已经无法继续执行下去,这时就出现了异常。

异常:概括来说就是因为程序出现错误,而在正常控制流意外采取的行为。所以异常主要分为两个阶段:

  • 发生了一个异常条件,当检测到错误并意识到异常条件,解释器就会引发一个异常。python也允许程序员自己引发异常。异常就是错误发生的信号,当前流被打断。
  • 对异常的处理发生在第二阶段,异常引发后,可以调用很多不同的操作,可以是忽略、减轻影响等等。

程序运行时发生的错误通常由于外部因素引起,例如非法输入或操作失败等等,这些因素并不在程序员的直接控制下,异常处理能在它们发生时采取更可靠的补救措施,提高应用程序的健壮性。

python中的异常

在此前的学习中,已经见识过大大小小不同的异常,这里列举几个常见异常,熟悉之后就能在程序出错时迅速定位到错误发生的位置。

  • NameError:尝试访问一个未声明的变量
  • ZeroDivisionError:除数为零
  • SyntaxError:python解释器语法错误。此异常是唯一不在运行时发生的异常,它代表python代码中有一个不正确的结构,在改正之前程序无法运行。
  • IndexError:请求的索引超出序列范围。在你尝试使用一个超出范围的值索引序列时引发。
  • KeyError:请求一个不存在的字典关键字。
  • IOError:输入/输出错误。例如尝试打开一个不存在的磁盘文件一类的操作会引发一个操作系统输入/输出错误。
  • AttributeError:尝试访问未知的对象属性。
  • AssertionError:断言异常。
  • 更多python标准异常
检测和处理异常

异常可以通过try语句来检测,任何在try语句块中的代码都会被检测。try语句的形式通常有两种,两种语句是互斥的,只能使用一种:

  • try-except:定义了异常监控的一段代码,并且提供了处理异常的机制。最常见的try语句由try块、except块和一个可选的错误原因组成。
# try:
#      try_suite        # 所要监控的异常
# except Exception[, reason]:
#      except_suite     # 异常处理代码
def safe_float(obj):
    try:
        retval=float(obj)
    except ValueError:
        retval='could not convert non-number to float'
    except TypeError:
        retval='type error'
    return retval

if __name__ == '__main__':
    try:
        f=open('file3','r')
    except IOError,e:	#只捕获IOError
        print 'cannot open file3',e

在程序运行时,解释器尝试执行try块里的代码,如果代码块完成后没有异常发生,执行流就会忽略except语句继续执行。而当except语句所指定的异常发生后,我们保存了错误的原因,控制流立即跳转到对应的处理器。

try中异常点之后的语句,永远不会执行。

try-except语句同样支持一个try多个except语句或在一个except中处理多个异常。

错误是无法避免的,try-except的作用时提供一个可以提示错误或处理错误的机制,而不是一个错误过滤器。避免吧大片代码装入try中然后使用pass忽略错误。

else语句:在except后使用,可以在try范围中没有异常被检测时,执行else子句。

finally语句:无论异常是否发生,子句中的代码都会被执行,finally子句可以和try-except语句一同使用.

  • try-finally:另一种使用finally的方法就是和try一起单独使用。try-finally并不是用来捕捉异常,而是用来维持一致的行为无论异常是否发生。
    当在try范围内产生一个异常时,会立即跳转到finally语句段,当finally中所有代码执行完毕后,会继续向上一层引发异常。
  • try…except…else…finally
上下文管理

with语句:python2中只有一些支持上下文协议的协议

  • file
  • decimal.Context
  • threading.LockType
  • threading.Lock
  • threading.RLock
  • threading.Condition
  • threading.Semaphore
  • threading.BoudedSemaphore
with open('file2''r') as f:
	for eachline in f:
		dosomething

上面这段代码就如书面意思,将file2打开并将文件对象赋值给f,无论在这一段代码的开始、中间还是结束时发生异常,会执行清理代码,此外文件仍会被自动的关闭。

触发异常

目前为止,所见到的异常都是由解释器引发的。python提供了raise语句可以明确的引发异常。
raise语句通常的写法是:raise[SomeException [, args [, traceback]]]
第一个参数是触发异常的名字,必须是一个字符串,类或实例。
第二个参数是可选的,来传给异常,可以是一个单独的对象或对象的元组。
第三个参数很少用到,有的话则是当异常触发时新生成的一个用于异常-正常化的追踪对象。

if __name__ == '__main__':
    x=10
    if x>5:
        raise ValueError("x cannot less than 10")

    try:
        raise NameError('Name error')
    except NameError:
        print 'this is except'
断言

断言是一句必须等价于布尔真的判定,相当于raise-if语句,测试一个表达式,如果返回值是假,则抛出一个异常。
如果发生返回值为假时,不采取其他操作,则会出发AssertionError。

assert 1==1
assert 2+2==2*3

AssertionError异常也可以用try-except语句捕捉。

下一章将讨论python中的面向对象编程。