`except` 子句中的名称绑定在子句后删除
问题描述
如何阻止 Python 删除名称绑定,当该名称是用于绑定捕获的异常?这是什么时候发生的变化行为进入 Python?
How can I stop Python from deleting a name binding, when that name is used for binding the exception that is caught? When did this change in behaviour come into Python?
我正在编写在 Python 2 和 Python 3 上运行的代码:
I am writing code to run on both Python 2 and Python 3:
exc = None
try:
1/0
text_template = "All fine!"
except ZeroDivisionError as exc:
text_template = "Got exception: {exc.__class__.__name__}"
print(text_template.format(exc=exc))
请注意,exc
在异常处理之前被显式绑定,因此 Python 知道它是外部范围内的名称.
Notice that exc
is explicitly bound before the exception handling, so Python knows it is a name in the outer scope.
在 Python 2.7 上,它运行良好,并且 exc
名称仍然可以用于format
调用::
On Python 2.7, this runs fine and the exc
name survives to be used in
the format
call::
Got exception: ZeroDivisionError
太好了,这正是我想要的:except
子句绑定了名称我可以在函数的其余部分使用该名称来引用异常对象.
Great, this is exactly what I want: The except
clause binds the name
and I can use that name in the rest of the function to refer to the
exception object.
在 Python 3.5 上,format
调用失败,因为显然 exc
绑定被删除::
On Python 3.5, the format
call fails because apparently the exc
binding is deleted::
Traceback (most recent call last):
File "<stdin>", line 8, in <module>
NameError: name 'exc' is not defined
为什么 exc
绑定会从外部作用域中删除?我们是什么意思可靠地保留名称绑定以在 except
之后使用它子句?
Why is the exc
binding deleted from the outer scope? How are we meant
to reliably preserve the name binding to use it after the except
clause?
此更改何时进入 Python,记录在哪里?
When did this change come into Python, where is it documented?
将其报告为 Python 3 中的错误是否正确?
Would I be right to report this as a bug in Python 3?
解决方案
不,这不是错误.您遇到的行为在 Python 3 中明确定义try
/except
语句 的文档.也给出了这种行为的原因:
No this is not a bug. The behavior you are experiencing is clearly and explicitly defined in the Python 3 documentation for the try
/except
statement. The reason for this behavior is also given:
当使用 as target
分配异常时,它会在 except
子句的末尾被清除.这好像
When an exception has been assigned using
as target
, it is cleared at the end of theexcept
clause. This is as if
except E as N:
foo
被翻译成
except E as N:
try:
foo
finally:
del N
这意味着必须将异常分配给不同的名称,以便能够在 except
子句之后引用它.异常被清除,因为附加了回溯,它们与堆栈帧形成一个引用循环,使该帧中的所有本地人保持活动状态,直到下一次垃圾回收发生.
This means the exception must be assigned to a different name to be able to refer to it after the except
clause. Exceptions are cleared because with the traceback attached to them, they form a reference cycle with the stack frame, keeping all locals in that frame alive until the next garbage collection occurs.
在 try
/except
块范围之外声明名称不起作用的原因是您在as
子句.这就是 Python 删除的名称.
The reason declaring the name outside of the scope of the try
/except
block didn't work is because you used exc
in the as
clause. So that was the name Python deleted.
解决方法是在 as
子句中使用不同的名称将异常绑定到,然后将全局变量分配给不同的异常名称:
The fix is to use a different name in the as
clause to bind the exception to, and then assign the global variable to the different exception name:
>>> exc_global = None
>>> try:
1 / 0
text_template = "All fine!"
except ZeroDivisionError as exc:
exc_global = exc
text_template = "Got exception: {exc.__class__.__name__}"
>>> print(text_template.format(exc=exc_global))
Got exception: ZeroDivisionError
正如 Anthony Sottile 在评论中指出的那样,try
/except
代码的反汇编也清楚地支持了文档的上述陈述:
As Anthony Sottile noted in the comments, the disassembly for the try
/except
code also clearly supports the above statements made by the documentation:
>>> code = """
try:
1/0
text_template = "All fine!"
except ZeroDivisionError as exc:
text_template = "Got exception: {exc.__class__.__name__}"
"""
>>> from dis import dis
>>> dis(code)
2 0 SETUP_EXCEPT 16 (to 18)
3 2 LOAD_CONST 0 (1)
4 LOAD_CONST 1 (0)
6 BINARY_TRUE_DIVIDE
8 POP_TOP
4 10 LOAD_CONST 2 ('All fine!')
12 STORE_NAME 0 (text_template)
14 POP_BLOCK
16 JUMP_FORWARD 38 (to 56)
5 >> 18 DUP_TOP
20 LOAD_NAME 1 (ZeroDivisionError)
22 COMPARE_OP 10 (exception match)
24 POP_JUMP_IF_FALSE 54
26 POP_TOP
28 STORE_NAME 2 (exc)
30 POP_TOP
32 SETUP_FINALLY 10 (to 44)
6 34 LOAD_CONST 3 ('Got exception: {exc.__class__.__name__}')
36 STORE_NAME 0 (text_template)
38 POP_BLOCK
40 POP_EXCEPT
42 LOAD_CONST 4 (None)
>> 44 LOAD_CONST 4 (None)
46 STORE_NAME 2 (exc)
48 DELETE_NAME 2 (exc)
50 END_FINALLY
52 JUMP_FORWARD 2 (to 56)
>> 54 END_FINALLY
>> 56 LOAD_CONST 4 (None)
58 RETURN_VALUE
相关文章