python - `except` 子句中的名称绑定(bind)在子句之后删除

标签 python python-3.x namespaces

<分区>

如何阻止 Python 删除名称绑定(bind),当该名称是 用于绑定(bind)捕获的异常?这是什么时候改变的 行为进入 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 在异常处理之前 显式绑定(bind),因此 Python 知道它是外部作用域中的名称。

在 Python 2.7 上,这运行良好并且 exc 名称保留下来用于 format 调用::

Got exception: ZeroDivisionError

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

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

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

为什么从外部作用域中删除了 exc 绑定(bind)?我们是什么意思 可靠地保留名称绑定(bind)以在 except 之后使用它 条款?

此更改何时进入 Python,记录在何处?

我将此报告为 Python 3 中的错误是否正确?

最佳答案

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

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

was translated to

except E as N:
    try:
        foo
    finally:
        del N

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 block 范围之外声明名称不起作用的原因是因为您在as 子句。所以这就是 Python 删除的名称。

修复方法是在 as 子句中使用不同的名称来绑定(bind)异常,然后将全局变量分配给不同的异常名称:

>>> 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

关于python - `except` 子句中的名称绑定(bind)在子句之后删除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45292479/

相关文章:

zend-framework - 如何在 Zend Framework 中使用命名空间?

python - 通过 lambda 将变量传递到函数 Tkinter

python - 如何对 aiohttp.web 应用程序进行单元测试

javascript - python函数可以像javascript一样在html中运行吗?

python - 主循环不等待 python 多处理池完成并卡住

python-3.x - 运行 pip install -rrequirements.txt 时出错

ruby-on-rails - 与 rails 中的模块和型号名称混淆

python - 如何将字符串列表写入文件并添加换行符?

python - 我不知道代码 "[:, row:row]"的含义

vb.net - 导入 : Should you, 不应该吗,语法和最佳实践