Python更改异常可打印输出,例如重载__builtins__

Ors*_*ong 8 python built-in python-3.x

我正在寻找一种方法将异常的可打印输出更改为愚蠢的消息,以便了解有关 python 内部结构的更多信息(并与朋友搞混;),但到目前为止还没有成功。

考虑以下代码

try:
   x # is not defined
except NameError as exc:
   print(exc)
Run Code Online (Sandbox Code Playgroud)

代码应输出 name 'x' is not defined

我希望将输出更改为the name 'x' you suggested is not yet defined, my lord. Improve your coding skills.

到目前为止,我明白你不能改变,__builtins__因为它们被“烘焙”为 C 代码,除非:

  1. 您使用 forbiddenfruit.curse 方法添加/更改任何对象的属性
  2. 您手动覆盖对象的字典

我已经尝试了两种解决方案,但没有成功:

禁果解决方案:

from forbiddenfruit import curse

curse(BaseException, 'repr', lambda self: print("Test message for repr"))
curse(BaseException, 'str', lambda self: print("Test message for str"))

try:
    x
except NameError as exc:
    print(exc.str()) # Works, shows test message
    print(exc.repr()) # Works, shows test message
    print(repr(exc)) # Does not work, shows real message
    print(str(exc)) # Does not work, shows real message
    print(exc) # Does not work, shows real message
Run Code Online (Sandbox Code Playgroud)

字典覆盖解决方案:

import gc

underlying_dict = gc.get_referents(BaseException.__dict__)[0]
underlying_dict["__repr__"] = lambda self: print("test message for repr")
underlying_dict["__str__"] = lambda self: print("test message for str")
underlying_dict["args"] = 'I am an argument list'

try:
    x
except NameError as exc:
    print(exc.__str__()) # Works, shows test message
    print(exc.__repr__()) # Works, shows test message
    print(repr(exc)) # Does not work, shows real message
    print(str(exc)) # Does not work, shows real message
    print(exc) # Does not work, shows real message
Run Code Online (Sandbox Code Playgroud)

AFAIK, usingprint(exc)应该依赖于__repr__or __str__,但似乎该print函数使用了其他东西,即使在阅读BaseExceptionvia 的所有属性时我也找不到print(dir(BaseException))。谁能告诉我print在这种情况下有什么用途?

[编辑]

要添加更多上下文:

我试图解决的问题一开始是为了和一个程序员朋友开玩笑,但现在成为我了解更多 python 内部结构的挑战。

我没有试图解决真正的业务问题,我只是想更深入地了解 Python 中的事物。 我很困惑,print(exc)不会使用BaseException.__repr____str__实际上。

[/编辑]

HTF*_*HTF 1

我将解释您所描述的行为:

  • exc.__repr__()

这只会调用您的 lambda 函数并返回预期的字符串。顺便说一句,您应该返回字符串,而不是在 lambda 函数中打印它。

  • print(repr(exc))

现在,这是一条不同的路线CPython,您可以在 GDB 会话中看到这一点,它是这样的:

Python/bltinmodule.c:builtin_repr将调用Objects/object.c:PyObject_Repr-在本例中,该函数将 用作获取和调用实现PyObject *v内置函数的函数的唯一参数。该函数将根据结构字段中的值格式化错误消息:repr()BaseException_reprargs

(gdb) p ((PyBaseExceptionObject *) self)->args 
$188 = ("name 'x' is not defined",)
Run Code Online (Sandbox Code Playgroud)

args值是Python/ceval.c:format_exc_check_arg根据NAME_ERROR_MSG同一文件中设置的宏设置的。

更新: UTC 2020 年 11 月 8 日(星期日)20:19:26

测试.py:

import sys
import dis


def main():
    try:
        x
    except NameError as exc:
        tb = sys.exc_info()[2]
        frame, i = tb.tb_frame, tb.tb_lasti
        code = frame.f_code
        arg = code.co_code[i + 1]
        name = code.co_names[arg]
        print(name)


if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

测试:

# python test.py
x
Run Code Online (Sandbox Code Playgroud)

笔记:

我还建议观看PyCon 2016 的这段视频。