Python:在异常实例中覆盖__str__

时间:2011-05-06 23:22:59

标签: python exception string

我试图在引发异常后覆盖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调用它。

我在这里缺少什么?

2 个答案:

答案 0 :(得分:11)

问题不在于__str__()没有被覆盖(就像你已经说过的那样),而是str(e)(无形中被打印调用)始终等同于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 覆盖动态派生新的异常类,并将异常更改为该类的实例。现在你的代码应该可以工作了。

答案 1 :(得分:2)

http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes指出“对于新式类,只有在对象的类型上定义,而不是在对象的实例字典中,才能保证特殊方法的隐式调用正常工作”。 IOW,您不能只为some_instance.__str__分配方法。此外,Monkey Patching不适用于内置类型,例如异常。你不会想要的,甚至不是非内置异常类,因为该补丁会改变该类的所有实例的行为。

如果你觉得有点hackish,你可以做一些像:

...
except DaUncoolException, e:
    e.args = ('cool override stuff!',) + e.args[1:]
    raise
但是,我不太喜欢这个。你为什么要做这样的事呢?