在子句之后删除的`except`子句中的名称绑定

big*_*ose 6 python namespaces python-3.x

当该名称用于绑定捕获的异常时,如何阻止Python删除名称绑定?这种行为改变何时进入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))
Run Code Online (Sandbox Code Playgroud)

请注意,在异常处理之前exc显式绑定,因此Python知道它是外部作用域中的名称.

在Python 2.7上,这运行正常,exc名称仍然可用于format调用::

Got exception: ZeroDivisionError
Run Code Online (Sandbox Code Playgroud)

太棒了,这正是我想要的:except子句绑定名称,我可以在函数的其余部分使用该名称来引用异常对象.

在Python 3.5上,format调用失败,因为显然删除exc 绑定::

Traceback (most recent call last):
  File "<stdin>", line 8, in <module>
NameError: name 'exc' is not defined
Run Code Online (Sandbox Code Playgroud)

为什么exc从外部范围删除绑定?我们如何在 条款之后可靠地保留名称绑定以使用它except

这个变化何时进入Python,它在哪里记录?

我是否应该将此报告为Python 3中的错误?

Chr*_*ean 6

不,这不是一个错误.您正在体验的行为try/ exceptstatementPython 3文档中清楚明确地定义.这种行为的原因还给出了:

使用时分配了一个例外as target,它在该except子句的末尾被清除.这就好像

except E as N:
   foo
Run Code Online (Sandbox Code Playgroud)

被翻译成

except E as N:
    try:
        foo
    finally:
        del N
Run Code Online (Sandbox Code Playgroud)

这意味着必须将异常分配给不同的名称才能在该except子句之后引用它.异常被清除,因为附加了回溯,它们与堆栈帧形成一个引用循环,使该帧中的所有本地生存,直到下一次垃圾收集发生.

声明try/ exceptblock 范围之外的名称不起作用的原因是因为您excas子句中使用了.这就是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
Run Code Online (Sandbox Code Playgroud)

正如Anthony Sottile在评论中指出的那样,try/ exceptcode 的反汇编也清楚地支持了文档的上述陈述:

>>> 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
Run Code Online (Sandbox Code Playgroud)