Skip to content

Commit 5923711

Browse files
committed
14.9小节完成
1 parent 011909f commit 5923711

File tree

1 file changed

+114
-104
lines changed

1 file changed

+114
-104
lines changed

source/c14/p09_raise_exception_in_response_to_another_exception.rst

Lines changed: 114 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -5,120 +5,130 @@
55
----------
66
问题
77
----------
8-
You want to raise an exception in response to catching a different exception, but want
9-
to include information about both exceptions in the traceback.
8+
你想捕获一个异常后抛出另外一个不同的异常,同时还得在异常回溯中保留两个异常的信息。
109

1110
|
1211
1312
----------
1413
解决方案
1514
----------
16-
To chain exceptions, use the raise from statement instead of a simple raise statement.
17-
This will give you information about both errors. For example:
18-
19-
>>> def example():
20-
... try:
21-
... int('N/A')
22-
... except ValueError as e:
23-
... raise RuntimeError('A parsing error occurred') from e...
24-
>>>
25-
example()
26-
Traceback (most recent call last):
27-
File "<stdin>", line 3, in example
28-
ValueError: invalid literal for int() with base 10: 'N/A'
29-
30-
The above exception was the direct cause of the following exception:
31-
32-
Traceback (most recent call last):
33-
File "<stdin>", line 1, in <module>
34-
File "<stdin>", line 5, in example
35-
RuntimeError: A parsing error occurred
36-
>>>
37-
38-
As you can see in the traceback, both exceptions are captured. To catch such an excep‐
39-
tion, you would use a normal except statement. However, you can look at the __cause__
40-
attribute of the exception object to follow the exception chain should you wish. For
41-
example:
42-
try:
15+
为了链接异常,使用 ``raise from`` 语句来代替简单的 ``raise`` 语句。
16+
它会让你同时保留两个异常的信息。例如:
17+
18+
::
19+
20+
>>> def example():
21+
... try:
22+
... int('N/A')
23+
... except ValueError as e:
24+
... raise RuntimeError('A parsing error occurred') from e
25+
>>>
4326
example()
44-
except RuntimeError as e:
45-
print("It didn't work:", e)
46-
47-
if e.__cause__:
48-
print('Cause:', e.__cause__)
49-
50-
An implicit form of chained exceptions occurs when another exception gets raised in‐
51-
side an except block. For example:
52-
53-
>>> def example2():
54-
... try:
55-
... int('N/A')
56-
... except ValueError as e:
57-
... print("Couldn't parse:", err)
58-
...
59-
>>>
60-
>>> example2()
61-
Traceback (most recent call last):
62-
File "<stdin>", line 3, in example2
63-
ValueError: invalid literal for int() with base 10: 'N/A'
64-
65-
During handling of the above exception, another exception occurred:
66-
67-
Traceback (most recent call last):
68-
File "<stdin>", line 1, in <module>
69-
File "<stdin>", line 5, in example2
70-
NameError: global name 'err' is not defined
71-
>>>
72-
73-
In this example, you get information about both exceptions, but the interpretation is a
74-
bit different. In this case, the NameError exception is raised as the result of a program‐
75-
ming error, not in direct response to the parsing error. For this case, the __cause__
76-
attribute of an exception is not set. Instead, a __context__ attribute is set to the prior
77-
exception.
78-
If, for some reason, you want to suppress chaining, use raise from None:
79-
80-
>>> def example3():
81-
... try:
82-
... int('N/A')
83-
... except ValueError:
84-
... raise RuntimeError('A parsing error occurred') from None...
85-
>>>
86-
example3()
87-
Traceback (most recent call last):
88-
File "<stdin>", line 1, in <module>
89-
File "<stdin>", line 5, in example3
90-
RuntimeError: A parsing error occurred
91-
>>>
27+
Traceback (most recent call last):
28+
File "<stdin>", line 3, in example
29+
ValueError: invalid literal for int() with base 10: 'N/A'
30+
31+
上面的异常是下面的异常产生的直接原因:
32+
33+
::
34+
35+
Traceback (most recent call last):
36+
File "<stdin>", line 1, in <module>
37+
File "<stdin>", line 5, in example
38+
RuntimeError: A parsing error occurred
39+
>>>
40+
41+
在回溯中科院看到,两个异常都被捕获。
42+
要想捕获这样的异常,你可以使用一个简单的 ``except`` 语句。
43+
不过,你还可以通过查看异常对象的 ``__cause__`` 属性来跟踪异常链。例如:
44+
45+
.. code-block:: python
46+
47+
try:
48+
example()
49+
except RuntimeError as e:
50+
print("It didn't work:", e)
51+
52+
if e.__cause__:
53+
print('Cause:', e.__cause__)
54+
55+
当在 ``except`` 块中又有另外的异常被抛出时会导致一个隐藏的异常链的出现。例如:
56+
57+
::
58+
59+
>>> def example2():
60+
... try:
61+
... int('N/A')
62+
... except ValueError as e:
63+
... print("Couldn't parse:", err)
64+
...
65+
>>>
66+
>>> example2()
67+
Traceback (most recent call last):
68+
File "<stdin>", line 3, in example2
69+
ValueError: invalid literal for int() with base 10: 'N/A'
70+
71+
在处理上述异常的时候,另外一个异常发生了:
72+
73+
::
74+
75+
Traceback (most recent call last):
76+
File "<stdin>", line 1, in <module>
77+
File "<stdin>", line 5, in example2
78+
NameError: global name 'err' is not defined
79+
>>>
80+
81+
这个例子中,你同时获得了两个异常的信息,但是对异常的解释不同。
82+
这时候,``NameError`` 异常被作为程序最终异常被抛出,而不是位于解析异常的直接回应中。
83+
84+
如果,你想忽略掉异常链,可使用 ``raise from None`` :
85+
86+
::
87+
88+
>>> def example3():
89+
... try:
90+
... int('N/A')
91+
... except ValueError:
92+
... raise RuntimeError('A parsing error occurred') from None...
93+
>>>
94+
example3()
95+
Traceback (most recent call last):
96+
File "<stdin>", line 1, in <module>
97+
File "<stdin>", line 5, in example3
98+
RuntimeError: A parsing error occurred
99+
>>>
92100

93101
|
94102
95103
----------
96104
讨论
97105
----------
98-
In designing code, you should give careful attention to use of the raise statement inside
99-
of other except blocks. In most cases, such raise statements should probably be
100-
changed to raise from statements. That is, you should prefer this style:
101-
102-
try:
103-
...
104-
except SomeException as e:
105-
raise DifferentException() from e
106-
107-
The reason for doing this is that you are explicitly chaining the causes together. That is,
108-
the DifferentException is being raised in direct response to getting a SomeExcep
109-
tion. This relationship will be explicitly stated in the resulting traceback.
110-
If you write your code in the following style, you still get a chained exception, but it’s
111-
often not clear if the exception chain was intentional or the result of an unforeseen
112-
programming error:
113-
114-
try:
115-
...
116-
except SomeException:
117-
raise DifferentException()
118-
119-
When you use raise from, you’re making it clear that you meant to raise the second
120-
exception.
121-
Resist the urge to suppress exception information, as shown in the last example. Al‐
122-
though suppressing exception information can lead to smaller tracebacks, it also dis‐
123-
cards information that might be useful for debugging. All things being equal, it’s often
124-
best to keep as much information as possible.
106+
在设计代码时,在另外一个 ``except`` 代码块中使用 ``raise`` 语句的时候你要特别小心了。
107+
大多数情况下,这种 ``raise`` 语句都应该被改成 ``raise from`` 语句。也就是说你应该使用下面这种形式:
108+
109+
::
110+
111+
try:
112+
...
113+
except SomeException as e:
114+
raise DifferentException() from e
115+
116+
这样做的原因是你应该显示的将原因链接起来。
117+
也就是说,``DifferentException`` 是直接从 ``SomeException`` 衍生而来。
118+
这种关系可以从回溯结果中看出来。
119+
120+
如果你像下面这样写代码,你仍然会得到一个链接异常,
121+
不过这个并没有很清晰的说明这个异常链到底是内部异常还是某个未知的编程错误。
122+
123+
.. code-block:: python
124+
125+
try:
126+
...
127+
except SomeException:
128+
raise DifferentException()
129+
130+
当你使用 ``raise from`` 语句的话,就很清楚的表明抛出的是第二个异常。
131+
132+
最后一个例子中隐藏异常链信息。
133+
尽管隐藏异常链信息不利于回溯,同时它也丢失了很多有用的调试信息。
134+
不过万事皆平等,有时候只保留适当的信息也是很有用的。

0 commit comments

Comments
 (0)