|
5 | 5 | ----------
|
6 | 6 | 问题
|
7 | 7 | ----------
|
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 | +你想捕获一个异常后抛出另外一个不同的异常,同时还得在异常回溯中保留两个异常的信息。 |
10 | 9 |
|
11 | 10 | |
|
12 | 11 |
|
13 | 12 | ----------
|
14 | 13 | 解决方案
|
15 | 14 | ----------
|
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 | + >>> |
43 | 26 | 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 | + >>> |
92 | 100 |
|
93 | 101 | |
|
94 | 102 |
|
95 | 103 | ----------
|
96 | 104 | 讨论
|
97 | 105 | ----------
|
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