`except`子句中的名称绑定在该子句之后删除 [英] Name binding in `except` clause deleted after the clause

查看:90
本文介绍了`except`子句中的名称绑定在该子句之后删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当名称为 用于绑定捕获的异常?这是什么时候改变的 行为进入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?

推荐答案

不,这不是bug. 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 the except 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子句中使用了exc.这就是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

这篇关于`except`子句中的名称绑定在该子句之后删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆