1.什么是异常
程序出现了错误(在编译或者执行过程中)
>>> a
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
a
NameError: name 'a' is not defined
NameError 表示我们访问了一个没有初始化的变量. 在 Python 解释器的符号表没有找到那个
变量. 任何可访问的变量必须在名称空间里列出. 访问变量需要由解释器进行搜索,
如果请求的名字没有在任何名称空间里找到, 那么将会生成一个 NameError 异常.
>>> 1/0
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
1/0
ZeroDivisionError: division by zero
任何数值被零除都会导致一个 ZeroDivisionError
异常.
2.常见异常种类
异常名称 | 描述 |
---|---|
BaseException | 所有异常的基类 |
SystemExit | 解释器请求退出 |
KeyboardInterrupt | 用户中断执行(通常是输入^C) |
Exception | 常规错误的基类 |
StopIteration | 迭代器没有更多的值 |
GeneratorExit | 生成器(generator)发生异常来通知退出 |
StandardError | 所有的内建标准异常的基类 |
ArithmeticError | 所有数值计算错误的基类 |
FloatingPointError | 浮点计算错误 |
OverflowError | 数值运算超出最大限制 |
ZeroDivisionError | 除(或取模)零 (所有数据类型) |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
EOFError | 没有内建输入,到达EOF 标记 |
EnvironmentError | 操作系统错误的基类 |
IOError | 输入/输出操作失败 |
OSError | 操作系统错误 |
WindowsError | 系统调用失败 |
ImportError | 导入模块/对象失败 |
LookupError | 无效数据查询的基类 |
IndexError | 序列中没有此索引(index) |
KeyError | 映射中没有这个键 |
MemoryError | 内存溢出错误(对于Python 解释器不是致命的) |
NameError | 未声明/初始化对象 (没有属性) |
UnboundLocalError | 访问未初始化的本地变量 |
ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 |
RuntimeError | 一般的运行时错误 |
NotImplementedError | 尚未实现的方法 |
SyntaxError | Python 语法错误 |
IndentationError | 缩进错误 |
TabError | Tab 和空格混用 |
SystemError | 一般的解释器系统错误 |
TypeError | 对类型无效的操作 |
ValueError | 传入无效的参数 |
UnicodeError | Unicode 相关的错误 |
UnicodeDecodeError | Unicode 解码时的错误 |
UnicodeEncodeError | Unicode 编码时错误 |
UnicodeTranslateError | Unicode 转换时错误 |
Warning | 警告的基类 |
DeprecationWarning | 关于被弃用的特征的警告 |
FutureWarning | 关于构造将来语义会有改变的警告 |
OverflowWarning | 旧的关于自动提升为长整型(long)的警告 |
PendingDeprecationWarning | 关于特性将会被废弃的警告 |
RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 |
SyntaxWarning | 可疑的语法的警告 |
UserWarning | 用户代码生成的警告 |
3.异常处理
简单介绍:
s = 'Hello girl!'
print s[100]
print 'continue'
如果我们没有对异常进行任何预防,那么在程序执行的过程中发生异常,就会中断程序,调用python默认的异常处理器,并在终端输出异常信息。这种情况下,第3行代码不会执行。
try…except
s = 'Hello girl!'
try:
print s[100]
except IndexError:
print 'error...'
print 'continue'
程序执行到第2句时发现try语句,进入try语句块执行,发生异常,回到try语句层,寻找后面是否有except语句。找到except语句后,会调用这个自定义的异常处理器。except将异常处理完毕后,程序继续往下执行。这种情况下,最后两个print语句都会执行。
except后面也可以为空,表示捕获任何类型的异常。
try…finally
s = 'Hello girl!'
try:
print s[100]
finally:
print 'error...'
print 'continue'
finally语句表示,无论异常发生与否,finally中的语句都要执行。但是,由于没有except处理器,finally执行完毕后程序便中断。这种情况下,倒第2个print会执行,到第1个不会执行。如果try语句中没有异常,三个print都会执行。
assert
assert False,'error...'
print 'continue'
这个语句,先判断assert后面紧跟的语句是True还是False,如果是True则继续执行print,如果是False则中断程序,调用默认的异常处理器,同时输出assert语句逗号后面的提示信息。本例情况下,程序中断,提示error,后面的print不执行。
with…as
with open('nothing.txt','r') as f:
f.read()
print 2/0
print 'continue'
我们平时在使用类似文件的流对象时,使用完毕后要调用close方法关闭,很麻烦。这里with…as语句提供了一个非常方便的替代方法:open打开文件后将返回的文件流对象赋值给f,然后在with语句块中使用。with语句块完毕之后,会隐藏地自动关闭文件。
如果with语句或语句块中发生异常,会调用默认的异常处理器处理,但文件还是会正常关闭。
这种情况下,会抛出异常,最后的print不执行。
详细介绍
可以通过编程来选择处理部分异常。看一下下面的例子,它会一直要求用户输入直到输入一个合法的整数为止,但允许用户中断这个程序(使用Control-C或系统支持的任何方法);注意用户产生的中断引发的是 KeyboardInterrupt 异常。
>>> while True:
... try:
... x = int(raw_input("Please enter a number: "))
... break
... except ValueError:
... print "Oops! That was no valid number. Try again..."
...
Try语句按以下方式工作。
- 首先,执行try 子句(try和except关键字之间的语句)。
- 如果未发生任何异常,忽略except 子句且try语句执行完毕。
- 如果在 try 子句执行过程中发生异常,跳过该子句的其余部分。如果异常的类型与except关键字后面的异常名匹配, 则执行 except 子句,然后继续执行try语句之后的代码。
- 如果异常的类型与 except 关键字后面的异常名不匹配,它将被传递给上层的try语句;如果没有找到处理这个异常的代码,它就成为一个未处理异常,程序会终止运行并显示一条如上所示的信息。
Try语句可能有多个异常子句,用来指定多个不同的异常。不过至多只有一个处理程序将被执行。处理程序只处理发生在相应 try 子句中的异常,不会处理同一个try子句的其他处理程序中发生的异常。一个 except 子句可以用带括号的元组列出多个异常的名字,例如:
... except (RuntimeError, TypeError, NameError):
... pass
注意,此元组周围的括号是必需的,因为except ValueError, e:是旧式的写法,在现代 Python 中通常写成 except ValueError as e: (如下所述)。为了保持向后兼容性,旧式语法仍然是支持的。这意味着except RuntimeError, TypeError不等同于except (RuntimeError, TypeError): 而等同于except RuntimeError as TypeError: , 这应该不是你想要的。
最后一个 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 子句,其出现时,必须放在所有 except 子句的后面。如果需要在 try 语句没有抛出异常时执行一些代码,可以使用这个子句。例如:
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子句比把额外的代码放在try子句中要好,因为它可以避免意外捕获不是由try ... except语句保护的代码所引发的异常。
当异常发生时,它可能带有相关数据,也称为异常的参数。参数的有无和类型取决于异常的类型。
except 子句可以在异常名(或元组)之后指定一个变量。这个变量将绑定于一个异常实例,同时异常的参数将存放在实例的args中。为方便起见,异常实例定义了__str__() ,因此异常的参数可以直接打印而不必引用.args。
也可以在引发异常之前先实例化一个异常,然后向它添加任何想要的属性。
>>> try:
... raise Exception('spam', 'eggs')
... except Exception as inst:
... print type(inst) # the exception instance
... print inst.args # arguments stored in .args
... print inst # __str__ allows args to be printed directly
... x, y = inst.args
... print 'x =', x
... print 'y =', y
...
<type 'exceptions.Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
对于未处理的异常,如果它含有参数,那么参数会作为异常信息的最后一部分打印出来。
异常处理程序不仅处理直接发生在 try 子句中的异常,而且还处理 try 子句中调用的函数(甚至间接调用的函数)引发的异常。例如:
>>> def this_fails():
... x = 1/0
...
>>> try:
... this_fails()
... except ZeroDivisionError as detail:
... print 'Handling run-time error:', detail
...
Handling run-time error: integer division or modulo by zero
引发异常
raise语句允许程序员强行引发一个指定的异常。例如:
>>> raise NameError('HiThere')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: HiThere
raise的唯一参数指示要引发的异常。它必须是一个异常实例或异常类(从Exception派生的类)。
如果你确定需要引发异常,但不打算处理它,一个简单形式的raise语句允许你重新引发异常:
>>> try:
... raise NameError('HiThere')
... except NameError:
... print 'An exception flew by!'
... raise
...
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
NameError: HiThere
用户定义的异常
程序可以通过创建新的异常类来命名自己的异常(Python 类的更多内容请参见类)。异常通常应该继承Exception类,直接继承或者间接继承都可以。例如:
>>> class MyError(Exception):
... def __init__(self, value):
... self.value = value
... def __str__(self):
... return repr(self.value)
...
>>> try:
... raise MyError(2*2)
... except MyError as e:
... print 'My exception occurred, value:', e.value
...
My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'
在此示例中,Exception默认的__init__()被覆盖了。新的行为简单地创建了value 属性。这将替换默认的创建args 属性的行为。
异常类可以像其他类一样做任何事情,但是通常都会比较简单,只提供一些属性以允许异常处理程序获取错误相关的信息。创建一个能够引发几种不同错误的模块时,一个通常的做法是为该模块定义的异常创建一个基类,然后基于这个基类为不同的错误情况创建特定的子类:
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
大多数异常的名字都以"Error"结尾,类似于标准异常的命名。
很多标准模块中都定义了自己的异常来报告在它们所定义的函数中可能发生的错误。类 这一章给出了类的详细信息。
定义清理操作
Try语句有另一个可选的子句,目的在于定义必须在所有情况下执行的清理操作。例如:
>>> try:
... raise KeyboardInterrupt
... finally:
... print 'Goodbye, world!'
...
Goodbye, world!
KeyboardInterrupt
不管有没有发生异常,在离开try语句之前总是会执行finally 子句。当try子句中发生了一个异常,并且没有except字句处理(或者异常发生在except或else子句中),在执行完finally子句后将重新引发这个异常。try语句由于break、contine或return语句离开时,同样会执行finally子句。以下是一个更复杂些的例子 (同时有except和finally字句的try语句的工作方式与 Python 2.5 一样):
>>> def divide(x, y):
... try:
... result = x / y
... except ZeroDivisionError:
... print "division by zero!"
... else:
... print "result is", result
... finally:
... print "executing finally clause"
...
>>> divide(2, 1)
result is 2
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
正如您所看到的,在任何情况下都会执行finally子句。由两个字符串相除引发的 TypeError异常没有被except子句处理,因此在执行finally子句后被重新引发。
在真实的应用程序中, finally子句用于释放外部资源(例如文件或网络连接),不管资源的使用是否成功。
清理操作的预定义
有些对象定义了在不需要该对象时的标准清理操作,无论该对象的使用是成功还是失败。看看下面的示例,它尝试打开一个文件并打印其内容到屏幕。
for line in open("myfile.txt"):
print line,
这段代码的问题就是代码执行完之后它还会让文件在一段不确定的时间内保持打开状态。这在简单的脚本中没什么,但是在大型应用程序中可能是一个问题。 With语句可以确保像文件这样的对象总能及时准确地被清理掉。
with open("myfile.txt") as f:
for line in f:
print line,
执行该语句后,文件f 将始终被关闭,即使在处理某一行时遇到了问题。其它对象是否提供了预定义的清理行为要查看它们的文档。