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

# ...

print(exc)

NameError:未定义名称“exc”

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

class Foo(object):
    Bar = Bar

但这也不能使它起作用:

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

有什么好的提示可以达到同样的效果吗?我不想写这样的东西:

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

# ...

print(exc)
4

1 回答 1

21

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

当使用 as target 分配了异常时,它会在 except 子句的末尾被清除

[...]

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

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

在 Python 2 中,异常没有对回溯的引用,这就是更改的原因。

然而,即使在 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
于 2014-06-17T19:23:12.993 回答