except-clause删除局部变量

Nik*_*s R 5 python scope python-3.x try-except

exc = None
try:
    raise Exception
except Exception as exc:
    pass

# ...

print(exc)
Run Code Online (Sandbox Code Playgroud)

NameError:未定义名称'exc'

这曾经在Python2中工作.为什么这样改变了?如果我至少可以重新分配exc,类似于类级属性

class Foo(object):
    Bar = Bar
Run Code Online (Sandbox Code Playgroud)

但这也不能使它工作:

exc = None
try:
    raise Exception
except Exception as exc:
    exc = exc
Run Code Online (Sandbox Code Playgroud)

有什么好的提示可以实现同样的目标吗?我不想写这样的东西:

exc = None
try:
    raise Exception("foo")
except Exception as e:
    exc = e

# ...

print(exc)
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 8

try语句明确限制了绑定异常的范围,以防止导致它泄漏的循环引用.请参阅try声明文档:

使用as作为目标分配异常时,将在except子句的末尾清除该异常.

[...]

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

强调我的; 请注意,您唯一的选择是将异常绑定到新名称.

在Python 2中,异常没有对traceback的引用,这就是为什么它被改变了.

但是,即使在Python 2中,也明确警告您要清理回溯,请参阅sys.exc_info():

警告:将回溯返回值分配给处理异常的函数中的局部变量将导致循环引用.这将阻止同一函数中的局部变量或回溯引用的任何内容被垃圾回收.由于大多数函数不需要访问回溯,因此最好的解决方案是使用类似的方法exctype, value = sys.exc_info()[:2]来仅提取异常类型和值.如果确实需要回溯,请确保在使用后删除它(最好用try ... finally语句完成)或调用exc_info()本身不处理异常的函数.

如果确实重新绑定了异常,则可能需要显式清除回溯:

try:
    raise Exception("foo")
except Exception as e:
    exc = e
    exc.__traceback__ = None
Run Code Online (Sandbox Code Playgroud)