如果它已经存在,为什么我必须导入回溯?

Why do I have to import traceback if it already exists?

如果我在 Python 中写了一些东西并且出了问题,我会自动得到一个回溯。

例如:

#!/usr/bin/env python

print("this will raise a division by zero exception")
print(2/0)

它会自动抛出异常回溯

>>> %Run test.py
this will raise a division by zero exception
Traceback (most recent call last):
  File "/home/pi/Desktop/test.py", line 4, in <module>
    print(2/0)
ZeroDivisionError: division by zero
>>> 

现在,假设我不希望程序崩溃并烧毁,但我想优雅地处理异常 - 比如我需要关闭文件或其他任何东西。

即:

#!/usr/bin/env python
import sys

try:
    print("Assume I'm doing something with a resource, like a file")
    print(". . . and something goes wrong\n")
    print("(This will raise a division by zero exception when I try to \"print(2/0)\")\n")
    print(2/0)

except BaseException as e:
    print(". . . and I want to handle the exception gracefully:\n")
    print("Oops! Something happened! (",e, ")\n")
    sys.exit(0)

这符合我的预期:

>>> %Run test.py
Assume I'm doing something with a resource, like a file
. . . and something goes wrong

(This will raise a division by zero exception when I try to "print(2/0)")

. . . and I want to handle the exception gracefully:

Oops! Something happened! ( division by zero )

─────────────────────────────────────────────────────────────────────────────────────────────────
Python 3.7.3 (/usr/bin/python3)
>>> 

但是,如果我尝试手动处理异常,我被告知为了获得系统提供的回溯,(错误的调用堆栈和位置),我必须导入回溯。

当我这样做时,我可以获得我想要的所有信息。

#!/usr/bin/env python
import sys
import traceback

try:
    print("Assume I'm doing something with a resource, like a file")
    print(". . . and something goes wrong\n")
    print("(This will raise a division by zero exception when I try to \"print(2/0)\")\n")
    print(2/0)

except BaseException as e:
    print(". . . and I want to handle the exception gracefully:\n")
    print("Oops! Something happened! (",e, ")\n")
    traceback.print_exc()
    sys.exit(0)

预期结果:

────────────────────────────────────────────────────────────────────────────────────────────────
Python 3.7.3 (/usr/bin/python3)
>>> %Run test.py
Assume I'm doing something with a resource, like a file
. . . and something goes wrong

(This will raise a division by zero exception when I try to "print(2/0)")

. . . and I want to handle the exception gracefully:

Oops! Something happened! ( division by zero )

Traceback (most recent call last):
  File "/home/pi/Desktop/test.py", line 9, in <module>
    print(2/0)
ZeroDivisionError: division by zero

─────────────────────────────────────────────────────────────────────────────────────────────────
Python 3.7.3 (/usr/bin/python3)
>>> 

“回溯”似乎已经存在,因为回溯在未处理的异常上是自动的

如果我使用 try/except 块,为什么我必须导入回溯,但如果不使用则不需要?

回溯反映了 Python 解释器在某些代码执行过程中给定点的状态。 Python 解释器显然可以访问自己的状态,并在抛出异常但未处理时通过打印错误消息与用户共享。它还允许代码访问它——通过 traceback 模块,这就是你需要导入它的原因。

当引发未在 try...except 中捕获的异常时,Python 的默认行为是打印回溯并退出。如果你确实抓住了它,那么默认值就不会发生。你必须决定如何处理它。如果您决定打印回溯,您现在必须通过导入回溯模块并使用那里的方法来明确地执行它。然而,这并不是绝对必要的,因为您可以只检查当前的回溯对象(来自 sys.exc_info()),然后编写您自己的格式化程序。

"Traceback" appears to already exist as the traceback is automatic on unhandled exceptions

您所看到的是 sys.excepthook 的默认行为,即在程序从未处理的异常退出之前打印回溯到 stderr。

亲身体验:

# example.py
import sys
sys.excepthook = lambda *args: None
print("before")
1/0
print("after")

上面的脚本将退出 non-zero,但不会打印回溯,因为挂钩已替换为 no-op。而“after”不会被打印出来,因为这个过程已经退出了。因此,对于未处理的异常,回溯并不完全是“自动”的,而是默认配置

通过调用 sys.exit(0),您有意抑制 ZeroDivisionError 异常。解释器将以零 return 代码退出,就 except 挂钩而言,您已经“处理”了 ZeroDivisionError 并选择退出回溯打印 (src).

traceback 存在,解释器可以使用它(例如打印它),但只有导入它才能使用它。如果您不使用 try/except 块,您将没有机会导入它。