概述
如果要显式地触发异常,可以使用raise
语句。它们的一般形式相当简单。一条raise
语句的组成包括raise
关键字,后面跟着可选的要引发的异常类或者异常类的一个实例:
raise instance #Raise instance of class
raise class #Make and raise instance of class:makes an instance
raise #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
...
as
在try
处理程序中是可选的(如果省略它,该实例则不会被赋值给一个名称),但是包含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'}
>>>X
99
因此,你通常应当在try
语句的except
分句中使用独特的变量名,即便它们已经局限在这一作用域中。如果你确实需要在try
语句后引用该异常实例,你只需将该实例赋值给另一个不会被自动移除的变量名即可:
>>> try:
... 1 / 0
... except Exception as X: #3.X localizes las'names to except block
... print(X)
... saveit = X
division by zero
>>> X
NameError:name'X"is not defined
>>> saveit
ZeroDivisionError('division by zero')
利用raise
传递异常
raise
语句拥有比我们目前所看到的更加丰富的性质。例如,一个不带异常名称或额外数据值的`raise命令的作用是重新引发当前异常。一般如果需要捕获和处理一个异常,又不希望异常在程序代码中死掉时,就会采用这种形式。
>>> try:
... raise IndexError('spam') # Exceptions remember arguments
... except IndexError:
... print('propagating')
... raise # Reraise most recent exception
propagating
IndexError Traceback (most recent call last)
<ipython-input-1-5ce28f2f34b4> in <module>()
1 try:
----> 2 raise IndexError('spam') # Exceptions remember arguments
3 except IndexError:
4 print('propagating')
5 raise # 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 exceptions
Traceback(most recent call last):
File "<stdin>",line 2,in <module>
ZeroDivisionError: division by zero
During 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 E
Traceback(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
这条语句能禁用前文中提到的连锁异常上下文的显示。其效果是在实际处理异常连锁的过程中,显示更少的在异常类型间转换的错误信息。
最后
以上就是害怕帅哥为你收集整理的系统学习Python——异常处理:raise语句的全部内容,希望文章能够帮你解决系统学习Python——异常处理:raise语句所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复