“ except” 语句或上下文管理器中“ as” 绑定的范围是什么?

Jos*_*uaF 7 python contextmanager

我知道一般来说 python 只会为类、函数等创建新的作用域,但我对astry/ except 块或上下文管理器中的语句感到困惑。在块内部分配的变量可以在块外部访问,这是有道理的,但是与as则不然。

所以这失败了:

try:
    raise RuntimeError()
except RuntimeError as error:
    pass

print(repr(error))
Run Code Online (Sandbox Code Playgroud)

但这成功了:

try:
    raise RuntimeError()
except RuntimeError as e:
    error = e

print(repr(error))
Run Code Online (Sandbox Code Playgroud)

与 绑定的变量发生了什么as,为什么正常的 python 作用域规则不适用?PEP表明它只是一个正常绑定的 python 变量,但情况似乎并非如此。

Kar*_*tel 8

正如PEP 3110以及当前文档as中所解释的,块中绑定的变量except会在块末尾显式且专门地清除,即使它们共享相同的本地范围。这提高了垃圾收集的即时性。该as语法最初不适用于 2.x 中的异常;它被向后移植到 2.6,但旧的语义被保留

这同样不适用于with

>>> from contextlib import contextmanager
>>> @contextmanager
... def test():
...     yield
... 
>>> with test() as a:
...     pass
... 
>>> a # contains None; does not raise NameError
>>> 
>>> def func(): # similarly within a function
...     with test() as a:
...         pass
...     return a
... 
>>> func()
>>> 
Run Code Online (Sandbox Code Playgroud)

该行为特定于except块,而不是特定于as关键字。


jua*_*aga 6

这是正常规则的记录例外,*特别适用于try-except语句,来自语言参考

当使用作为目标分配异常时,它会在 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 - except复合语句,as不是独立语句,它是不同的独立复合语句(withtry-except)的一部分