600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 系统学习Python——异常处理:raise语句

系统学习Python——异常处理:raise语句

时间:2020-12-22 09:29:25

相关推荐

系统学习Python——异常处理:raise语句

如果要显式地触发异常,可以使用raise语句。它们的一般形式相当简单。一条raise语句的组成包括raise关键字,后面跟着可选的要引发的异常类或者异常类的一个实例:

raise instance #Raise instance of classraise class#Make and raise instance of class:makes an instanceraise#Reraise the most recent exception

如前所述,从Python 2.6和Python 3.0以后异常总是类的实例。因此,这里第一种raise形式是最常见的。我们直接提供了一个实例,该实例要么是在raise之前创建的,要么就是raise语句中创建的。如果我们传入一个类,则Python会对这个类调用不带参数的默认构造函数,以此来创建被引发的一个异常实例;这个格式等同于在类引用后面添加圆括号。最后的一种形式会重新引发最近触发的异常;它通常用于异常处理程序中,用来传递已经捕获的异常。

Python 3.X中不再支持raise Exc,Args的形式,不过该形式在Python 2.X仍然可用。在Python 3.X中,需要使用我们介绍的raise实例构造调用形式。在Python 2.X中等价的逗号形式是一种语法的历史遗留物,目的是为了兼容现在已经废除的基于字符串的异常模型,这一模型在Python 2.X已经不复存在。如果被用到的话,这一形式将会被转换成Python 3.X的调用形式。

在早期的Python版本中,有一种raise Exc的形式也能用于命名一个类,它在各个版本中都能被转换成raise ExC(),来调用类的无参数构造函数。除了被废除的逗号语法之外,Python 2.X的raise语句还能同时支持字符串和类异常。但是前者在Python 2.6中被移除并在2.5中被弃用,所以在我们不会详细介绍,现在我们需要使用新的基于类的异常。

引发异常

为了更清楚地说明,让我们看一些例子。对于内置异常,如下两种形式是等价的,两者都会引发指定的异常类的一个实例,但是第一种形式是隐式地创建实例:

raise IndexError #Class(instance created)raise IndexError() #Instance(created in statement)

我们也可以提前创建实例,因为raise语句接受任何类型的对象引用,下面的例子像刚才一样引发了IndexError

exc = IndexError() #Create instance ahead of time raise exc excs = [IndexError,TypeError]raise excs[o]

当引发一个异常的时候,Python把引发的实例与该异常一起发送。如果一个try包含了一个except name as X:形式的分句,那么raise中的异常实例就会赋值给变量X

try:...except IndexError as X:#X assigned the raised instance object ...

astry处理程序中是可选的(如果省略它,该实例则不会被赋值给一个名称),但是包含as将使得处理程序能够访问异常实例中的数据以及异常类中的方法。

这种模型对于我们用类编写的用户定义的异常也同样有效。例如,下面的代码向异常类的构造函数中传入了参数,从而使该参数可以通过异常实例的赋值在处理程序中被使用:

class MyExc(Exception): pass...raise MyExc('spam’)#Exception class with constructor args ...try:...except MyExc as X: #Instance attributes available in handler print(X.args)

不管你如何指定异常,异常总是通过实例对象来识别的,而且在程序运行的任意时刻,至多只能有一个处于激活状态的异常实例。一旦异常在程序中某处被一条except分句捕获,它就不会再传递给另一个try语句了,除非它被另一个raise语句或错误再次引发。

作用域和try except变量

我们将在后面的文章中深入学习异常对象。在Python 2.X中,一个except分句中的异常引用变量名不单单局限于分句本身,也就是说这一变量在except对应的代码块运行结束后,仍然可用。相反,Python 3.X会将异常引用名局限在对应的except块中——在块执行结束后该变量将不能再被使用,这和3.X中推导表达式的临时循环变量非常相似,还有前面提到的,Python 3.X不接受Python 2.X中的except分句逗号语法:

c:\code>py-3>>> try:...1 / 0... except Exception, X:SyntaxError:invalid syntax>>> try:...1 / 0... except Exception as X: #3.X localizes las'names to except block...print(X)division by zero>>> X NameError:name'X"is not defined

然而不同于推导循环变量的是,这一变量在Python 3.X的except块退出后就被移除了。之所以这么做的原因在于如果不这么做,这一变量将继续持有Python运行时调用栈的一个引用,这会导致垃圾回收机制被推迟同时占用了额外的内存空间。即使你在别处使用了这一异常引用名,这种移除也将被强制执行,而且是一种比推导语法还要严格的策略:

>>> X=99>>> try:... 1 / 0... except Exception as X: #3.X localizes andremoves on exit!... print(x)division by zero>>> X NameError:name'X'is not defined>>> X=99>>> {X for X in'spam'}#2.X/3.X localizes only:not removed{'s','a','p','m'}>>>X99

因此,你通常应当在try语句的except分句中使用独特的变量名,即便它们已经局限在这一作用域中。如果你确实需要在try语句后引用该异常实例,你只需将该实例赋值给另一个不会被自动移除的变量名即可:

>>> try:...1 / 0... except Exception as X: #3.X localizes las'names to except block...print(X)...saveit = Xdivision by zero>>> X NameError:name'X"is not defined>>> saveitZeroDivisionError('division by zero')

利用raise传递异常

raise语句拥有比我们目前所看到的更加丰富的性质。例如,一个不带异常名称或额外数据值的`raise命令的作用是重新引发当前异常。一般如果需要捕获和处理一个异常,又不希望异常在程序代码中死掉时,就会采用这种形式。

>>> try:... raise IndexError('spam') # Exceptions remember arguments... except IndexError:... print('propagating')... raise# Reraise most recent exceptionpropagatingIndexError Traceback (most recent call last)<ipython-input-1-5ce28f2f34b4> in <module>()1 try:----> 2raise IndexError('spam')# Exceptions remember arguments3 except IndexError:4print('propagating')5raise# Reraise most recent exception

如果这样执行ra1se时,就能重新引发异常,并将其传递给更高层的处理程序(或者顶层的默认处理程序,它会停止程序并打印标准出错消息)。

Python 3.X异常链:raise from

异常有时能作为对其他异常的响应而被触发:它既可以是有意地被触发,也可以是由于其他新的程序错误而被触发。为了在这些情况中支持完全的开放性,Python 3.X也允许raise语句拥有一个可选的from分句:

raise newexception from otherexception

from分句被使用在一个显式raise请求中的时候,from后面跟的表达式指定了另一个异常类或实例,该异常(即上面的otherexception.)会附加到新引发异常(即上面的newexception)的__cause__属性。如果新引发的异常没有被捕获,那么Python会把两个异常都作为标准出错消息的一部分打印出来:

>>> try:... 1 / 0...except Exception as E:... raise TypeError('Bad!') from E # Explicitly chained exceptions Traceback (most recent call last):File "<stdin>",line 2,in <module>ZeroDivisionError: division by zero The above exception was the direct cause of the following exeeption:Traceback(most recent call last):File "<stdin>",line 4,in <module>TypeError: Bad

当在一个异常处理程序内部,由程序错误隐式地引发一个异常的时候,一个相似的过程会紧接着自动发生。前一个异常会添加到新异常的__context__属性中,并且如果该异常未捕获的话,则同样会显示在标准出错消息中:

>>> try:... 1 / 0... except:...badname # Implicitly chained exceptionsTraceback(most recent call last):File "<stdin>",line 2,in <module>ZeroDivisionError: division by zeroDuring handling of the above exception, another exception occurred:Traceback(most recent call last):File "<stdin>",line 4,in <module>NameError:name 'badname' is not defined

在上述两种情况中,因为原先异常在被添加到新异常对象的同时,自身可能也有添加的原因(更早的异常),所以异常的因果链可以为任意长度,并都会被完整地打印在错误信息中。也就是说,错误信息可以包含多于两条的异常。最终的效果是,在隐式和显式上下文中都能让程序员知道所有涉及的异常,只要一个异常触发了另一个:

>>> try:... try:... raise IndexError()... except Exception as E:... raise TypeErro() from E ... except Exception as E:... raise SyntaxError() from ETraceback(most recent call last):File "<stdin>",line 3,in <module>IndexError The above exception was the direct cause of the following exception:Traceback(most recent call last):File "<stdin>",line 5,in <module>TypeError The above exception was the direct cause of the following exception:Traceback(most recent call last):File "<stdin>",line 7,in <module>SyntaxError:None

同理,下面的代码会显示三条异常,尽管它们都是隐式产生的:

>>> try:... try:... 1 / 0... except:... badname... except:open('nonesuch')

与合并try语句一样,连锁引发的异常和其他语言中的用法相似(包括Java和C#),然而我们很难说这些语言之间到底是谁借鉴了谁。在Python中,这是一个高级的并且多少还有些难懂的扩展。事实上,Python3.3新增了种能够阻止异常连锁引发的方式:Python 3.3禁用连锁异常:raise from None。Python 3.3引人了一种新的语法形式使用None作为from分句的异常名称:

raise newexception from None

这条语句能禁用前文中提到的连锁异常上下文的显示。其效果是在实际处理异常连锁的过程中,显示更少的在异常类型间转换的错误信息。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。