本章主题:
什么是异常
Python中的异常
探测和处理异常
上下文管理
引发异常
断言
标准异常
创建异常
相关模块
什么是异常
- 错误
从软件方面来说,错误是语法或是逻辑上的。语法错误只是软件的结构上有错误,导致不能被解释器解释或者编译器无法编译。这些错误必须在程序执行前被纠正。
当程序的语法正确之后,剩下的就是逻辑错误了。逻辑错误可能是由于不完整或是不合法的输入所致,在其他情况下,还可能是逻辑无法生成、计算、或是输出结果需要的过程无法执行。这些错误通常被称为域错误和范围错误。
当Python检测到一个错误时,解释器会指出当前流已经无法继续执行下去。这个时候就出现异常。
- 异常
对异常最好的描述是:它是因为程序出现了错误而在正常控制流以外采取的行为。这个行为又分为两个阶段:首先是引起异常发生的错误,然后是检测(和采取可能的措施)阶段。
python中的异常
- NameError :尝试访问一个未申明的变量
>>> foo
Traceback (innermost last):
File "<stdin>",line 1, in ?
NameError: name 'foo' is not defined
#NameError 表示我们访问了一个没有初始化的变量。在Python解释器的符号表没有找到那个令人讨厌的变量,我们将在后面的两章讨论名称空间,现在大家可以认为它们是连接名字和对象的“地址簿”就可以了
#任何可访问的变量必须在名称空间里列出,访问变量需要由解释器进行搜索,如果请求的名字没有在任何名称空间里找到,那么将会生成一个NameError异常
- ZeroDivisionError : 除数为零
>>> 1/0
Traceback (inner last):
File"<stdin>",line 1,in ?
ZeroDivisionError: integer division or modulo by zero
#任何数值被零除都会导致一个ZeroDivisionError异常
- SyntaxError: Python 解释器语法错误
>>> for
File "<string>", line 1
for
^
SyntaxError: invalid syntax
#SyntaxError 异常时唯一不是在运行时发生的异常。它代表Python
代码中有一个不正确的结构,在它改正之前程序是无法执行的。这些错误一般都是在编译时发生,Python解释器无法把你脚本转化为python字节码。也有可能是你导入了一个有缺陷的模块。
- IndexError : 请求的索引超出序列范围
>>> alist = []
>>> alist[0]
Traceback (innermost last):
File "<stdin>", line 1,in ?
IndexError: last index out of range
#IndexError 在你尝试使用一个超出范围的值索引序列引发。
- KeyError :请求一个不存在的字典的关键字
>>> aDict = {'host': 'earth','port': 80}
>>> print aDict['server']
Traceback (innermost last):
File "<stdin>", line 1, in ?
KeyError: server
#映射对象,例如字典,是依靠关键字(key)访问数据的,如果使用了错误的或者是不存在的键请求字典会引发一个KeyError异常。
- IOError : 输入/输出错误
>>> f = open ("blah")
Traceback (innermost last):
File "<stdin>",line 1, in ?
IOError: [Errno 2] No such file or directory: 'blah'
#类似尝试打开一个不存在的磁盘文件一类的操作会引起一个操作系统输入/输出(I/O)错误。任何类型的I/O错误都会引发IOError异常
- AttributeError : 尝试访问未知的对象属性
>>> class myClass(object) :
... pass
...
>>> myInst = myClass()
>>> myInst.bar = 'spam'
>>> myInst.bar
'spam'
>>> myInst.foo
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError foo
#在这个例子里,我们在myInst.bar存储了一个值,也就是实例myInst的bar属性,属性被定义后,我们可以使用梳洗的点属性操作符访问它,但如果是没有定义属性,例如我们访问foo属性,将导致一个AttributeError异常。
检测和处理异常
异常可以通过try语句来检测。任何在try语句块里的代码都会被检测,检查有无异常发生。
try语句有二种形式: try-except和try-finally 这两个语句是互斥的,也就是说你只能使用其中的一种。一个try语句可以对应一个或者多个except子句,但只能对应一个finally子句,或者是一个try-except-finally复合语句。
try-except语句
try:
try_suite #监控这里的异常
except Exception[,reason]:
except_suite #异常处理代码
>>> try:
... f = open ('blah','r')
... except IOError,e:
... print 'could not open file:',e
...
cloud not open file: [Errno 2] No such file or directory
处理多个异常的except语句
#我们还可以在一个except子句里处理多个异常。except语句在处理多个异常要求异常被放在一个元祖里:
except (Exception1,Exception2)[,reason]:
suite_for_Exception1_and_Exception2
def safe_float(object):
try:
retval = float(object)
except (ValueError,TypeError):
retval = 'argument must be a number or numberic string'
return retval
捕获所有异常
#way1
#
try:
:
except Exception, e:
#error occurred,log 'e',etc.
#way2
#
try:
:
except:
#error occurred,etc
#关于捕获所有异常,你应当知道有些异常不是由错误条件引起的,它们是SystemExit和KeyboardInterupt。
#SystemExit是由于当前Python应用程序需要退出
#KeyboardInterrupt是代表用户按下了Ctrl+C,想要退出Python。在真正需要的时候,这些异常却会被异常捕获。
异常被迁移到新式类(new-style class)启用了一个新的“所有异常之母”,这个类叫做 BaseException,异常的继承结构有了少许的调整,为了让人们摆脱不得不除创建两个处理器惯用法。
- BaseException
| - KeyboardInterrupt
| - SystemExit
| - Exception
| - (all other current bulit-in exception) 所有内建异常
try:
:
except Exception, e:
#handle real errors
#如果你确实要捕获所有异常,那么你需要使用新的 BaseException:
try:
:
except BaseException, e:
#handle all errors
异常参数
异常也可以有参数,异常引发后它会被传递给异常处理器。当异常被引发后参数是作为附加帮助信息传递给异常处理器的。虽然异常原因是可选的,但标准内建异常提供至少一个参数,指示异常原因的一个字符串。
异常的参数可以在处理器里忽略,但是Python提供了保存这个值得语法。我们已经在上边接触到相关内容:要想访问提供的异常原因,你必须保留一个变量来保存这个参数。把这个参数放在except语句后,接在要处理的异常后面。except语句的这个语法可以被扩展为:
# single exception
except Exception[,reason]:
suite_for_Exception_with_Argument
# multiple exception
except (Exception1,Exception2,...,ExceptionN)[,reason]:
suite_for_Exception1_to_ExceptionN_with_Argument
#reason将会是一个包含来自导致异常的代码的诊断信息的类实例。异常参数自身会组织一个元组,并存储为类实例(异常类的实例)的属性
无论reason只包含一个字符串或是由错误编号和字符串组成的元组,调用str(reason)总会返回一个良好可读的错误原因。不要忘记reason是一个类实例——这样做你其实是调用类的特殊方法str()
信用卡交易系统(cardrun.py)
#!/usr/bin/env python
def safe_float(obj):
'safe version if float()'
try:
retval = float(obj)
except (ValueError,TypeError), diag:
retval = str(diag)
return retval
def main():
'handle all the data processing'
log = open('cardlog.txt', 'w')
try:
ccfile = open('carddata.txt', 'r')
except IOError, e:
log.write('no txns this month\n')
log.close()
return
txns = ccfile.readlines()
ccfile.close()
total = 0.00
log.write('account log:\n')
for eachTxn in txns:
result = safe_float(eachTxn)
if isinstance(result,float):
total += result
log.write('data... processed\n')
else:
log.write('ignored: %s' %(result))
print '$%.2f (new balance)' % (total)
log.close()
if __name__ = '__main__':
main()
else 子句
我们已经看过else语句段配合其他python语句,比如条件和循环。至于try-except语句段,它的功能和你所见过的其他else没有太多的不同: 在try范围中没有异常被检测到时,执行else子句
import 3rd_party_module
log = open('logfile','w')
try:
3rd_party_module.function()
except:
log.write("*** caught exception in module \n")
else:
log.write("*** no exceptions caught\n")
log.close()
finally 子句
finally 子句是无论异常是否发生,是否捕捉都会执行的一段代码
下面是try-except-else-finally的语法实例:
try:
A
except MyException:
B
else:
C
finally:
D
#无论如何,你都可以有不止一个的except子句,但最少有一个except语句,而else和finally都是可选的。无论异常发生在A、B或C都 将执行finally块。
通过try-finally来实现关闭文件而无论错误是否发生
#方式1
try:
try:
ccfile = open('carddata.txt')
txns = ccfile.readlines()
except IOError:
log.write('no txns this month\n')
finally:
ccfile.close()
#方式2
try:
try:
ccfile = open('carddata.txt')
txns = ccfile.readlines()
finally:
ccfile.close()
except IOError:
log.write('no txns this month\n')
上下文管理
-
with语句
类似于 try-except-finally,with语句也是用来简化代码的,这与try-except和try-finally所想达到的目的前呼后应。try-except和try-finally的一种特定的配合用法是保证共享资源的唯一分配,并在任务结束后释放它。比如文件(数据、日志、数据库等等)、线程资源、简单同步、数据库连接,等等。
然而,with语句的目的在于从流程图中把try、except、finally关键字和资源分配释放相关代码统统去掉,而不是像try-except-finally那样仅仅简化代码使之易用
#with语法的基本语法如下:
with context_expr [as var]:
with_suite
创建异常(myexc.py)
#!/usr/bin/env python
#
import os
import socket
import errno
import types
import tempfile
class NetworkError(IOError):
pass
class FileError(IOError):
pass
def updArgs(args,newarg=None):
if isinstance(args,IOError):
myargs = []
myargs.extend([arg for arg in args])
else:
myargs = list(args)
if newarg:
myargs.append(newarg)
return tuple(myarg)
def fileArgs(file, mode, args):
if args[0] == errno.EACCES and 'access' in dir(os):
perms = ''
permd = { 'r': os.R_OK,'w': os.W_OK,'x': os.X_OK}
pkeys = permd.keys()
pkeys.sort()
pkeys.reverse()
for eachPerm in 'rmx':
if os.access(file, permd[eachPerm])
perms += eachPerm
else:
perm += '-'
if isinstance(args,IOError):
myargs = []
myargs.extend([arg for arg in args])
else:
myargs = list(args)
myargs[1] = " '%s' %s (perms: '%s')"% (mode, myargs[1], perms)
myargs.append(args.filename)
else:
myargs = args
return tuple(myargs)
def myconnect(sock, host, port):
try:
sock.connect((host, port))
except socket.error, args:
myargs = updArgs(args)
if len(myargs) == 1:
myargs = (error.ENXIO, myargs[0])
raise NetworkError, updArgs(myargs, host + ': ' + str(port))
def myopen(file, mode='r'):
try:
fo = open(file, mode)
except IOError, args:
raise FileError, fileArgs(file, mode, args)
return fo
def testfile():
file = mktemp()
f = open(file,'w')
f.close()
for eachTest in ((0, 'r'), (0100, 'r'),(0400, 'w'), (0500, 'w'))
try:
os.chmod(file, eachTest[0])
f = open(file, eachTest[1])
except FileError, args:
print "%s: %s" % (args.__class__.__name__, args)
else:
print file, "opened ok... perm ignored"
f.close()
os.chmod(file, 0777)
os.unlink(file)
def testnet():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
for eachHost in ('deli', 'www'):
try:
myconnect(s, 'deli', 8080)
except NetworkError, args:
print "%s: %s" % (args.__class__.__name__, args)
if __name__ == "__main__":
testfile()
testnet()