首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python更改可打印的异常输出(如重载__builtins__ )

Python更改可打印的异常输出(如重载__builtins__ )
EN

Stack Overflow用户
提问于 2020-10-30 15:13:25
回答 3查看 480关注 0票数 8

我正在寻找一种方法,将异常的可打印输出更改为一条愚蠢的消息,以便更多地了解python内部(并与朋友打交道;),到目前为止都没有成功。

考虑以下代码

代码语言:javascript
复制
try:
   x # is not defined
except NameError as exc:
   print(exc)

代码应输出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. 手动重写对象的字典。

我尝试过这两种解决方案,但都没有成功:

违禁水果溶液:

代码语言:javascript
复制
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

字典重写解决方案:

代码语言:javascript
复制
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

使用print(exc)的AFAIK应该依赖于__repr____str__,但似乎print函数使用的是其他东西,即使在通过print(dir(BaseException))读取BaseException的所有属性时也找不到。有人能告诉我print在这种情况下的用途吗?

编辑

要添加更多的上下文:

我想要解决的问题最初是和程序员朋友开玩笑,但现在变成了一个挑战,让我更多地了解python的内部结构。

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

/EDIT

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-11-08 16:23:53

我只想解释一下你描述的行为:

  • exc.__repr__()

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

  • print(repr(exc))

现在,在CPython中这是一个不同的路径,您可以在GDB会话中看到这一点,如下所示:

Python/bltinmodule.c:builtin_repr将调用Objects/object.c:PyObject_Repr -此函数获取PyObject *v作为它将用于获取和调用实现内置函数repr()BaseException_repr的唯一参数。此函数将根据来自args结构字段的值格式化错误消息:

代码语言:javascript
复制
(gdb) p ((PyBaseExceptionObject *) self)->args 
$188 = ("name 'x' is not defined",)

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

更新:太阳11月8日20:19:26 2020年世界协调时

test.py:

代码语言:javascript
复制
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()

测试:

代码语言:javascript
复制
# python test.py
x

注:

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

票数 1
EN

Stack Overflow用户

发布于 2020-11-08 14:39:09

Intro

我会用一种更关键的方法来解释为什么你会想做你想做的事。

Python为您提供了处理特定异常的能力。这意味着,如果您有业务问题,您将使用特定的异常类,并为该特定情况提供自定义消息。现在,记住这一段,让我们继续,我将在后面提到这个。

TL;DR

现在,让我们自上而下:

使用except Exception捕获所有类型的错误通常不是一个好主意,如果您想要捕获的话,比如说变量名错误。你会用except NameError代替。这就是为什么它有一条能完美描述这个问题的默认消息。所以假设你会按给定的方式使用它。这些被称为具体异常。

现在,对于您的具体情况,请注意别名as exc。通过使用别名,您可以访问传递给异常对象的参数,包括默认消息。

代码语言:javascript
复制
try:
   x # is not defined
except NameError as exc:
   print(exc.args)

运行该代码(我将其放在app.py中),您将看到:

代码语言:javascript
复制
$ python app.py
("name 'x' is not defined",)

这些args作为一个系列传递给异常(列表,或者在本例中是不可变的元组列表)。

这就产生了一种想法,即可以轻松地将参数传递给异常的构造函数(__init__)。在您的例子中,"name 'x' is not defined"是作为一个参数传递的。

您只需提供一条自定义消息,就可以利用它来解决您的问题,例如:

代码语言:javascript
复制
try:
   x # is not defined
except NameError as exc:
   your_custom_message = "the name 'x' you suggested is not yet defined, my lord. Improve your coding skills"
   # Now, you can handle it based on your requirement:
   #  print(your_custom_message)
   #  print(NameError(your_custom_message))
   #  raise NameError(your_custom_message)
   #  raise NameError(your_custom_message) from exc

输出现在是您想要实现的。

代码语言:javascript
复制
$ python app.py
the name 'x' you suggested is not yet defined, my lord. Improve your coding skills

还记得第一段吗,我说过我以后会提到它?我提到了为特定情况提供自定义消息。如果您希望在处理与产品相关的特定变量的名称错误时构建自己的库,则假定用户将使用可能引发NameError异常的代码。他们很可能会用except Exception as excexcept NameError as exc捕捉到它。当他们做print(exc)时,他们现在就会看到你的信息。

摘要

我希望这对您来说是有意义的,只需提供一条自定义消息并将其作为参数传递给NameError,或者只是打印它。海事组织,最好是把它和你使用的东西一起学习。

票数 3
EN

Stack Overflow用户

发布于 2020-11-08 00:07:10

像这样的错误被硬编码到解释器中(无论如何,在CPython的情况下,这很可能是您正在使用的)。您将无法更改从Python内部打印的消息。

当CPython解释器试图查找名称时执行的C源代码可以在这里找到:https://github.com/python/cpython/blob/master/Python/ceval.c#L2602。如果要更改名称查找失败时打印的错误消息,则需要在同一个文件中更改这条线

代码语言:javascript
复制
#define NAME_ERROR_MSG \
    "name '%.200s' is not defined"

编译修改后的源代码将产生一个Python解释器,当遇到未定义的名称时,该解释器将打印您的自定义错误消息。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64611050

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档