Python: raise / 3 key elements of an exception
1. raise
-statement in Python 2
形式定义是 raise [arg1[, arg2[, arg3]]]
,分这么几种情况:
1.1 raise
nothing(也就是 raise
后面什么都不接)
- In this case,
raise
statement re-raises the last exception that was active in the current scope. - If no exception is active in the current scope, a
TypeError
exception is raised indicating that this is an error.- If running under IDLE, a
Queue.Empty
exception is raised instead.
- If running under IDLE, a
所谓 “last exception that was active in the current scope”,大概的场景是:
try:
# do something dangerous
except SomeException as e: # 即使你没有 `as e`,`raise` 也是可以定位到你当前的 exception instance
raise
# ----- IS EQUIVALENT TO ----- #
# raise e
此外,这个当前 scope 下 active 的 exception 可以通过 sys.exc_info()
来获取。这个函数返回一个 tuple,(exc_type, exc_value, tb_obj)
,也就是我们的异常三要素。
如果 raise
后面有参数,它一定会 evaluate 出一个 (exc_type, exc_value, tb_obj)
,空缺的值用 None
填充。
- 注意形式定义里的
arg3
即是tb_obj
(一个 built-in 类型Traceback
的对象)- 它的作用是 be substituted as the place where the exception occurre instead of the current location
- 如果你
arg3
不是Traceback
类型,系统会抛一个TypeError
(arg1, arg2)
并不严格对应(exc_type, exc_value)
,因为raise
语法的性质导致(所以我不喜欢 python 2!)- 而且我一直以为
exc_value
指 error msg,其实不是的,它本质一定是一个 exception instance
- 而且我一直以为
1.2 raise arg1
单独抛出一个 exception instance e
,此时 (exc_type, exc_value) == (type(e), e)
。
tb_obj
在异常发生时系统会自动创建一个给你,所以我估计是很少会需要去自己管理这个对象,所以这里我们也忽略这个对象的值。
import sys
ve = ValueError()
try:
raise ve
except ValueError:
print(sys.exc_info())
# Output: (<type 'exceptions.ValueError'>, ValueError(), <traceback object at 0x7fb8d03d97a0>)
import sys
ve = ValueError("1 != 2")
try:
raise ve
except ValueError:
print(sys.exc_info())
# Output: (<type 'exceptions.ValueError'>, ValueError('1 != 2',), <traceback object at 0x7fb8d03d97a0>)
1.3 raise arg1, arg2
涉及到异常类型继承关系时,这一句的 evaluation 有点微妙。
用法一: raise exc_type, constructor_param
或者 raise exc_type, (constructor_params...)
此时 exc_type = arg1
;arg2
可以是单个值或是一个 tuple,用来传递给 exc_type
的 constructor,从而 exc_value = arg1(arg2)
import sys
try:
raise ValueError, "1 != 2"
except ValueError:
print(sys.exc_info())
# Output: (<type 'exceptions.ValueError'>, ValueError('1 != 2',), <traceback object at 0x7fb8d03d9758>)
try:
raise ValueError, ('1', '!=', '2')
except ValueError:
print(sys.exc_info())
# Output: (<type 'exceptions.ValueError'>, ValueError('1', '!=', '2'), <traceback object at 0x7fb8d03d9a28>)
用法二: raise exc_type, exc_value
此时 exc_type = arg1
;如果 isinstance(arg2, arg1) == True
,则 exc_value = arg2
,否则 fall back 到用法一。
import sys
class MyValueError(ValueError):
pass
class MyIOError():
pass
try:
raise ValueError, MyValueError()
except ValueError:
print(sys.exc_info())
# Output: (<class '__main__.MyValueError'>, MyValueError(), <traceback object at 0x7fb8d03d9b90>)
try:
raise ValueError, MyIOError()
except ValueError:
print(sys.exc_info())
# Output: (<type 'exceptions.ValueError'>, ValueError(<__main__.MyIOError instance at 0x7fb8d03d98c0>,), <traceback object at 0x7fb8d03d95a8>)
2. raise
-statement in Python 3
形式定义是 raise [arg1 [from arg2]]
,而且在没有参数对应 tb_obj
,这是因为 python 3 的 exception 自带 setter __traceback__()
,而且是 return self
,所以可以这么连写:
raise Exception("foo occurred").with_traceback(tb_obj) [from Xxx]
2.1 raise
nothing
基本等同于 python 2 的情况,不同之处在于:
- If no exception is active in the current scope, a
RuntimeError
exception is raised indicating that this is an error.- python 2 是抛
TypeError
- python 2 是抛
2.2 raise arg1
arg1
must be either a subclass or an instance of BaseException
.
- If it is a class,
exc_type = arg1
andexc_value = arg1()
(一定是使用无参 constructor). - If it is an instance,
exc_type = type(arg1)
andexc_value = arg1
2.3 raise arg1 from arg2
The from
clause is used for exception chaining: if given, arg2
must be another exception class or instance, which will then be attached to the raised exception as the __cause__
attribute (which is writable). If the raised exception is not handled, both exceptions will be printed:
>>> try:
... print(1 / 0)
... except Exception as exc:
... raise RuntimeError("Something bad happened") from exc
...
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 exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened
A similar mechanism works implicitly if an exception is raised inside an except
or a finally
clause: the previous exception is then attached as the new exception’s __context__
attribute:
>>> try:
... print(1 / 0)
... except:
... raise RuntimeError("Something bad happened")
...
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>
RuntimeError: Something bad happened
Exception chaining can be explicitly suppressed by specifying from None
:
>>> try:
... print(1 / 0)
... except:
... raise RuntimeError("Something bad happened") from None
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened
Comments