SECTION 13 错误和异常(二)

SECTION 13 错误和异常(二)

错误和异常(二) 13.1 上下文管理13.2 触发异常13.2.1 raise 语句13.2.2 raise 语句的用法13.2.3 raise示例 13.3 断言13.4 创建异常13.5 为什么用异常(现在)?13.6 异常和 sys 模块13.7 相关模块

13.1 上下文管理

with 语句 with 语法的基本用法看上去如下:

with context_expr [as var]: with_suite

你不能对Python 的任意符号使用 with 语句.它仅能工作于支持上下文管理协议(context management protocol)的对象.这显然意味着只有内建了”上下文管理”的对象可以和 with 一起工作.我们过一会再来阐明它的含义.

支持该协议的对象

file decimal.Context thread.LockType threading.Lock threading.RLock threading.Condition threading.Semaphore threading.BoundedSemaphore

with open(‘/etc/passwd’, ‘r’) as f:for eachLine in f:# …do stuff with eachLine or f…

它会完成准备工作,比如试图打开一个文件,如果一切正常,把文件对象赋值给 f.然后用迭代器遍历文件中的每一行,当完成时,关闭文件.无论的在这一段代码的开始,中间,还是结束时发生异常,会执行清理的代码,此外文件仍会被自动的关闭.

13.2 触发异常

到目前为止,我们所见到的异常都是由解释器引发的.由于执行期间的错误而引发.程序员在编写 API 时也希望在遇到错误的输入时触发异常,为此,Python 提供了一种机制让程序员明确的触发异常:这就是 raise 语句.

13.2.1 raise 语句

raise 语句对所支持是参数十分灵活,对应到语法上就是支持许多不同的格式.rasie 一般的用法是:

raise [SomeException [, args [, traceback]]] 第一个参数,SomeExcpetion,是触发异常的名字.如果有,它必须是一个字符串,类或实例(详见下文).如果有其他参数(arg 或 traceback),就必须提供 SomeExcpetion.第二个符号为可选的 args(比如参数,值),来传给异常.这可以是一个单独的对象也可以是一个对象的元组.当异常发生时,异常的参数总是作为一个元组传入.如果 args 原本就是元组,那么就将其传给异常去处理;如果 args 是一个单独的对象,就生成只有一个元素的元组(就是单元素元组).大多数情况下,单一的字符串用来指示错误的原因.如果传的是元组,通常的组成是一个错误字符串,一个错误编号,可能还有一个错误的地址,比如文件,等等.最后一项参数,traceback,同样是可选的(实际上很少用它),如果有的话,则是当异常触发时新生成的一个用于异常-正常化(exception—normally)的追踪(traceback)对象.当你想重新引发异常时,第三个参数很有用(可以用来区分先前和当前的位置).如果没有这个参数,就填写 None.最常见的用法为 SomeException 是一个类.不需要其他的参数,但如果有的话,可以是一个单一对象参数,一个参数的元组,或一个异常类的实例.如果参数是一个实例,可以由给出的类及其派生类实例化(已存在异常类的子集).若参数为实例,则不能有更多的其他参数. 13.2.2 raise 语句的用法 rasie 语法描述raise exclass触发一个异常,从 exclass 生成一个实例(不含任何异常参数)raise exclass()同上,除了现在不是类;通过函数调用操作符(function calloperator:“()”)作用于类名生成一个新的 exclass 实例,同样也没有异常参数raise exclass, args同上,但同时提供的异常参数 args,可以是一个参数也可以元组raise exclass(args)同上raise exclass,args, tb同上,但提供一个追踪(traceback)对象 tb 供使用raise exclass,instance通过实例触发异常(通常是 exclass 的实例);如果实例是 exclass的子类实例,那么这个新异常的类型会是子类的类型(而不是exclass);如果实例既不是 exclass 的实例也不是 exclass 子类的实例,那么会复制此实例为异常参数去生成一个新的 exclass 实例.raise instance通过实例触发异常:异常类型是实例的类型;等价于raiseinstance.class,instance(同上).raise string(过时的) 触发字符串异常raise string, args同上,但触发伴随着 argsraise string, args, tb同上,但提供了一个追踪(traceback)对象 tb 供使用raise(1.5 新增)重新触发前一个异常,如果之前没有异常,触发 TypeError.13.2.3 raise示例 >>> def test(n):… try:… 100/n… except Exception as res:… print(“catch except”)… raise… >>> test(0)catch exceptTraceback (most recent call last): File “”, line 1, in File “”, line 3, in testZeroDivisionError: integer division or modulo by zero >>> def test(n):… if n==100:… raise… print n… >>> test(100)Traceback (most recent call last): File “”, line 1, in File “”, line 3, in testTypeError: exceptions must be old-style classes or derived from BaseException, not NoneType 13.3 断言

断言是一句必须等价于布尔真的判定;此外,发生异常也意味着表达式为假.如果你刚刚接触断言这个概念,无妨.断言可以简简单单的想象为raise-if 语句(更准确的说是raise-if-not 语句).测试一个表达式,如果返回值是假,触发异常.

断言语句 断言语句等价于这样的 Python 表达式:如果断言成功不采取任何措施(类似语句),否则触发AssertionError(断言错误)的异常. assert 的语法如下:

assert expression[, arguments] >>> assert 1==1>>> assert 1==2Traceback (most recent call last): File “”, line 1, in AssertionError

AssertionError 异常和其他的异常一样可以用 try-except 语句块捕捉,但是如果没有捕捉,它将终止程序运行而且提供一个如下的 traceback:

Traceback (most recent call last): File “”, line 1, in AssertionError

下面是我们如何用 try-except 语句捕获 AssertionError 异常:

try: assert 1 == 0, ‘One does not equal zero silly!’except AssertionError, args: print ‘%s: %s’ % (args.__class__.__name__, args)

从命令行执行上面的代码会导致如下的输出:

AssertionError: One does not equal zero silly!

为了让你更加了解 assert 如何运作,想象一下断言语句在 Python 中如何用函数实现.可以像下面这样:

def assert(expr, args=None): if __debug__ and not expr: raise AssertionError, args 13.4 创建异常

尽管标准异常集包含的内容已经相当广泛,你还是可以创建自己的异常.一种情况是你想在特定的标准异常和模块异常中添加额外的信息.

我们将介绍两个例子,都与 IOError 有关.IOError 是一个用于输入/输出的通用异常,可能在无效的文件访问或其他形式的通信中触发.假如我们想要更加明确的标明问题的来源,比如:对于文件错误,我们希望有行为类似 IOError 的一个 FileError 异常,但是名字表明是在执行文件操作.我们将查看的另一个异常与套接字(socket)网络编程有关.socket模块生成的异常叫socket.error,不是内建的异常.它从通用 Exception 类派生.然而socket.error 这个异常的宗旨和IOError 很类似,所以我们打算定义一个新的从 IOError 派生的 NetworkError 的异常,但是其包含了socket.error 提供的信息.

代码:

#!/usr/bin/python2.7import os, socket, errno, types, tempfileclass NetworkError(IOError): passclass FileError(IOError): passdef 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(myargs)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 ‘rwx’: if os.access(file, permd[eachPerm]): perms += eachPerm else: perms += ‘-‘ 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)# conv inst2tuple if len(myargs) == 1: myargs = (errno.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 fodef testfile(): file = tempfile.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 = myopen(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)# enable all perms 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() c:usersadmini~1appdatalocaltemptmpp2ledi opened ok… perm ignoredc:usersadmini~1appdatalocaltemptmpp2ledi opened ok… perm ignored===[Errno 13] Permission denied: ‘c:\users\admini~1\appdata\local\temp\tmpp2ledi’FileError: [Errno 13] Permission denied===[Errno 13] Permission denied: ‘c:\users\admini~1\appdata\local\temp\tmpp2ledi’FileError: [Errno 13] Permission deniedNetworkError: [Errno 11001] getaddrinfo failed: ‘deli:8080’NetworkError: [Errno 11001] getaddrinfo failed: ‘deli:8080’ 13.5 为什么用异常(现在)?

运行环境必须足够强健,来处理应用级别的错误,并提供用户级别的错误信息.就服务器而言,这必须转化为一个”非错误” .即使面对一个错误, 应用应该成功的中止, 不至于灾难性的影响其执行环境. Python 异常处理促使成熟和正确的编程.

13.6 异常和 sys 模块

另一种获取异常信息的途径是通过 sys 模块中 exc_info()函数. 此功能提供了一个 3 元组(3-tuple)的信息, 多于我们单纯用异常参数所能获得. 让我们看看如何用 sys.exc_info() :

>>> try:… float(‘abc123’)… except:… import sys… exc_tuple = sys.exc_info()…>>> print exc_tuple(, ,)>>>>>> for eachItem in exc_tuple:… print eachItem… exceptions.ValueErrorinvalid literal for float(): abc123

我们从 sys.exc_info()得到的元组中是:

exc_type: 异常类 exc_value: 异常类的实例 exc_traceback: 追踪(traceback)对象

13.7 相关模块

异常相关的标准库

模块描述exceptions内建异常(永远不用导入这个模块)contextliba为使用 with 语句的上下文对象工具sys包含各种异常相关的对象和函数(见 sys.ex*)


比丘资源网 » SECTION 13 错误和异常(二)

发表回复

提供最优质的资源集合

立即查看 了解详情