【问题标题】:Name binding in `except` clause deleted after the clause [duplicate]子句后删除的`except`子句中的名称绑定[重复]
【发布时间】:2017-07-25 01:36:18
【问题描述】:

如何阻止 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))

请注意,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 中的错误是否正确?

【问题讨论】:

    标签: python python-3.x namespaces


    【解决方案1】:

    不,这不是错误。您遇到的行为在Python 3 documentation for the try/except statement 中明确定义。也给出了这种行为的原因:

    当使用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 在 cmets 中指出的那样,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
    

    【讨论】:

    • 这个反汇编也很清楚(把它扔到一个函数中,运行dis.dis(f))——它本质上将exc = None; del exc添加到finally块的末尾
    • @AnthonySottile 好点。我会将其添加到我的答案中。
    猜你喜欢
    • 2014-08-06
    • 2014-07-09
    • 1970-01-01
    • 2019-07-30
    • 1970-01-01
    • 2020-10-28
    • 2011-09-20
    • 1970-01-01
    • 2019-03-05
    相关资源
    最近更新 更多