8 错误和异常(Errors and Exceptions)
到现在为止,还没怎么提过错误消息,不过如果你试过一些示例,可能会看到一些。有两种不同类型的 错误:语法错误和例外
Table of Contents
1 语法错误
语法错误可能是你还在学习python时最为常见的错误,例如
>>> while True print "hi~" File "<stdin>", line 1 while True print "hi~" ^ SyntaxError: invalid syntax
有一个箭头指向最早发现错误的地方,这里指向了print,因为Ture后面少了冒号
2 异常(Exceptions)
即使语句和表达式语法正确,在执行时也可能出现错误,这种错误称为异常(Exceptions)。 异常并不都是致命的,你马上会学到如何处理他们。 许多异常程序都不处理,而是返回一个错误消息,例如:
>>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: integer division or modulo by zero >>> 4 + git*3 Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'git' is not defined >>> '2' + 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: cannot concatenate 'str' and 'int' objects >>>
错误消息的最后一行就是异常消息,冒号前是异常的类型。上面的 ZeroDivisionError, NameError, TypeError, 都是系统内置的异常。
3 处理异常
可以自己编写程序来处理异常,比如下面这个例子,它会返回异常,直到用户输入有效数据为止。
>>> while True: ... try: ... x = int(raw_input("Please enter a number: ")) ... break ... except ValueError: ... print "Oops! That was no valid number. Try again..." ... Please enter a number: x Oops! That was no valid number. Try again... Please enter a number: 32x Oops! That was no valid number. Try again... Please enter a number: 038
- 使用 try 和 except ExceptionName 来处理异常
- 如果没有异常产生,except 段会被跳过
- 如果某处有异常产生,后面的语句会被跳过,如果产生的异常类型和except后的类型一致,except后的语句会被执行
- 如果发生异常,但和except后的类型不一致,异常会传递到try语句外面,如果没有相应处理,那么就会打印出像上 一个例子那样的信息。
一个try语句可能有多个except与之对应,分别处理不同类型的异常,最多只有一种处理会被执行。一个except可以包含多 个类型名,比如:
... except (RuntimeError, TypeError, NameError): ... pass
注意上面的三种异常类型,必须用括号把它们括起来,因为在现代python中, except ValueError, e 的意思是 except ValueError as e:(后面会讲这是什么意思)
最后一个except一般不指定名字,用于处理其余情况
import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except IOError as e: print "I/O error({0}): {1}".format(e.errno, e.strerror) except ValueError: print "Could not convert data to an integer." except: print "Unexpected error:", sys.exc_info()[0] raise
try..except 语句还可以选择使用else,例如
for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print 'cannot open', arg else: print arg, 'has', len(f.readlines()), 'lines' f.close()
需要注意,一旦使用else,每个except后都要有else,这种方式用于需要指定某一异常不出现时执行什么操作。
except子句可以在异常名后指定参数,这些参数被存储在异常实例产生时的 instance.arg
>>> try: ... raise Exception('spam', 'eggs') ... except Exception as inst: ... print type(inst) ... print inst.args ... print inst ... x, y = inst.args ... print 'x =', x ... print 'y =', y ... <type 'exceptions.Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs
异常处理不仅仅处理直接在try中出现的异常,还可以处理在try中调用函数的异常
>>> def mdiv(): ... x = 1/0 ... >>> try: ... mdiv() ... except ZeroDivisionError as detail: ... print 'Handling run-time error:', detail ... Handling run-time error: integer division or modulo by zero
4 raise 异常
raise 语句允许强制一个异常发生
>>> raise NameError('hi,there') Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: hi,there
raise 的参数必须是一个异常实例或者一个异常类
5 用户自定义异常
程序可以通过创建一个异常类来命令一个新的异常,这个异常类需要通过直接或者间接的方式由 Exception 类派生。
>>> class MyError(Exception): ... def __init__(self, value): ... self.value = value ... def __str__(self): ... return repr(self.value) ... >>> try: ... raise MyError(1+5) ... except MyError as e: ... print 'My exception occurred, value:', e.value ... My exception occurred, value: 6 >>> raise MyError('oops!') Traceback (most recent call last): File "<stdin>", line 1, in <module> __main__.MyError: 'oops!'
在上面的例子中,__ init __ (), 覆盖了默认的 init 函数,新的行为创建了 value 属性, 替换了原有的 创建args属性的行为。
其它类可以做的事情,通过定义Exception类都可以完成。但是Exception类总是被设计地非常简单, 它们提供一些属性,这样错误处理时就可能方便地提取出这些属性。 当设计一个模块处理多种异常时,常常先定义一个基本的类,其它类在此基础上处理一些特殊情况。
class Error(Exception): """Base class for exceptions in this module.""" pass class InputError(Error): """Exception raised for errors in the input. Attributes: expr -- input expression in which the error occurred msg -- explanation of the error """ def __init__(self, expr, msg): self.expr = expr self.msg = msg class TransitionError(Error): """Raised when an operation attempts a state transition that's not allowed. Attributes: prev -- state at beginning of transition next -- attempted new state msg -- explanation of why the specific transition is not allowed """ def __init__(self, prev, next, msg): self.prev = prev self.next = next self.msg = msg
6 定义清除(clean-up)操作
try语句还可以定义清除操作,用于无论任何时候都需要被执行的语句。
>>> try: ... raise KeyboardInterrupt ... finally: ... print 'Goodbye, world!' ... Goodbye, world! Traceback (most recent call last): File "<stdin>", line 2, in <module> KeyboardInterrupt
无论是否发生异常,finally后的语句在离开try之前都会被执行。
下面看一个复杂点的例子,
>>> def divide(x, y): ... try: ... result = x / y ... except ZeroDivisionError: ... print "division by zero!" ... else: ... print "result is", result ... finally: ... print "excecuting finally clause" ... >>> divide (3,2) result is 1 excecuting finally clause >>> divide (3,0) division by zero! excecuting finally clause >>> divide ("3","1") excecuting finally clause Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in divide TypeError: unsupported operand type(s) for /: 'str' and 'str'
在实例应用中,finally
7 预定义的 清除(Clean-up) 操作
一些对象预先定义了清除操作,当不再需要对象时这些清除操作会被执行,无论使用此对象的操作是否成功。
例如下面的例子:
for line in open("myfile.txt"): print line,
上述例子的问题在于,程序执行完后,文件对象仍然没有关闭。
with语句可以使得file这样的对象被及时正确合理地清除
with open("myfile.txt") as f: for line in f: print line,