当名称是 用于绑定捕获的异常?这是什么时候改变的 行为进入 Python?
我正在编写在Python 2和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 知道它是外部作用域中的一个名称。
在 Python 2.7 上,这运行良好,并且exc
名称可以保留下来用于format
调用:
Got exception: ZeroDivisionError
太好了,这正是我想要的:except
子句绑定名称 我可以在函数的其余部分使用该名称来引用 异常对象。
在 Python 3.5 上,format
调用失败,因为显然exc
绑定已删除::
Traceback (most recent call last):
File "<stdin>", line 8, in <module>
NameError: name 'exc' is not defined
为什么从外部范围中删除exc
绑定?我们的意思是 可靠地保留名称绑定,以便在except
之后使用它 第?
这种变化是什么时候进入 Python 的,它在哪里记录?
我将其报告为 Python 3 中的错误是正确的吗?
不,这不是错误。您遇到的行为在try
/except
语句的 Python 3 文档中明确定义。还给出了此行为的原因:
当使用
as target
分配异常时,将在except
子句的末尾清除该异常。这好像except E as N: foo
被翻译成
except E as N: try: foo finally: del N
这意味着必须将异常分配给不同的名称,以便能够在
except
子句之后引用它。异常被清除,因为附加了回溯,它们与堆栈帧形成一个参考循环,使该帧中的所有局部变量保持活动状态,直到下一次垃圾回收发生。
在try
/except
块的范围之外声明名称不起作用的原因是您在as
子句中使用了exc
。所以这就是Python删除的名称。
解决方法是在as
子句中使用不同的名称将异常绑定到,然后将全局变量分配给不同的异常名称:
>>> 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
代码的反汇编也清楚地支持文档所做的上述陈述:
>>> 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