【问题标题】:Python: override __str__ in an exception instancePython:在异常实例中覆盖 __str__
【发布时间】:2011-05-06 23:22:58
【问题描述】:

在引发异常后,我正在尝试覆盖 Python 中 Exception 子类的打印输出,但我没有运气让我的覆盖被实际调用。

def str_override(self):
    """
    Override the output with a fixed string
    """

    return "Override!"

def reraise(exception):
    """
    Re-raise an exception and override its output
    """

    exception.__str__ = types.MethodType(str_override, exception, type(exception))

    # Re-raise and remove ourselves from the stack trace.
    raise exception, None, sys.exc_info()[-1]

def test():
    """
    Should output "Override!" Actually outputs "Bah Humbug"
    """
    try:
        try:
            raise Exception("Bah Humbug")
        except Exception, e:
            reraise(e, "Said Scrooge")
    except Exception, e:
        print e

知道为什么这实际上并没有覆盖 str 方法吗?反省实例变量表明该方法实际上被该方法覆盖,但它就像 Python 只是拒绝通过 print 调用它。

我在这里错过了什么?

【问题讨论】:

    标签: python exception string


    【解决方案1】:

    问题不在于__str__() 没有被覆盖(就像你已经说过的那样,它确实如此),而是str(e)(它被 print 无形地调用)是不是 始终等同于e.__str__()。更具体地说,如果我做对了,str()(和其他特殊方法,例如 repr())不会在实例字典中查找 str - 它只会在类字典。至少所谓的新型类(Python 3.x IIRC 中唯一的类)就是这种情况。您可以在此处阅读更多信息:

    http://mail.python.org/pipermail/python-bugs-list/2005-December/031438.html

    如果您想为重新引发的异常更改异常错误消息,您可以执行以下操作:

    def reraise(exception):
        """
        Re-raise an exception and override its output
        """
    
        exType = type(exception)
        newExType = type(exType.__name__ + "_Override", (exType,), { '__str__': str_override})
        exception.__class__ = newExType
    
        # Re-raise and remove ourselves from the stack trace.
        raise exception, None, sys.exc_info()[-1]
    

    这将动态派生一个具有 str 覆盖的新异常类,并将异常更改为该类的实例。现在你的代码应该可以工作了。

    【讨论】:

    • 实际上,仔细观察它不会:它改变了异常的类别。正如我在下面提到的,我基本上是在更改深度类的异常消息,我需要保持它的外在行为。更改类意味着我必须更改处理此异常的任何代码,我不想这样做。
    • 其实……也许会。 grin 抱歉来来回回:您的回答中有一些有趣的微妙之处。如果我正确理解了代码,那么您正在创建一个动态 SUBCLASS,它仍应保留与外界的原始异常接口。 ie// 提高A;再加注 B(A); catch A:也应该捕获 B 的实例。光滑!
    • @James:是的,这就是它背后的想法。 :) A 的子类应始终被 catch A: 捕获。
    • 就像我说的:光滑!谢谢。
    • 这太棒了。我以前从未想过以这种方式动态派生类型。感谢您的帖子。
    【解决方案2】:

    http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes 声明“对于新型类,特殊方法的隐式调用只有在对象类型上定义时才能保证正常工作,而不是在对象的实例字典中”。 IOW,您不能只为some_instance.<strong>__<em>str</em>__</strong> 分配一个方法。此外,Monkey Patching 不适用于异常等内置类型。无论如何你都不想要,即使是非内置异常类,因为该补丁会改变该类的所有实例的行为。

    如果你觉得有点 hackish,你可以改为:

    ...
    except DaUncoolException, e:
        e.args = ('cool override stuff!',) + e.args[1:]
        raise
    

    不过,我不太喜欢这个。你为什么要做这样的事情?

    【讨论】:

    • 我在一个 500KSLOC 程序中有一个较旧的类,它深埋在应用程序中,并抛出了在堆栈更高处处理的异常。我想将信息附加到该类引发的每个异常。
    • 您的解决方案似乎有效(并保持异常类与抛出的相同)。知道附加到 args 的可靠性有多高吗?依赖大多数在消息中某处打印 args 的内容是否安全?
    • 如果e 的类是exception.BaseException 的子类并且没有人弄乱内部结构,那么它应该可以工作。但请注意,在 Python 2.x 中,任何旧样式类都可以作为异常引发,即使它没有子类化内置异常类。那些可能没有args 属性。
    猜你喜欢
    • 1970-01-01
    • 2018-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 2016-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多