2011-05-06 111 views
4

我想在异常被引发后覆盖Python中的Exception子类的打印输出,并且我没有运气让我的覆盖被实际调用。Python:在例外情况下重写__str__

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 

知道为什么这实际上并不覆盖海峡方法?内省实例变量显示该方法实际上被方法重写,但它就像Python只是拒绝通过打印来调用它。

缺少什么我在这里?

回答

10

的问题不在于__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] 

这将动态地推导一个新的异常类将覆盖,并将异常更改为该类的一个实例。现在你的代码应该工作。

+0

这看起来像会起作用。谢谢! – James 2011-05-07 17:33:05

+0

实际上,仔细观察它不会:它会改变异常类。正如我在下面提到的,我基本上正在改变深层类的异常消息,我需要保持它的外在行为。更改类意味着我必须更改处理此异常的任何代码,我不想那样做。 – James 2011-05-07 20:36:56

+0

其实......也许会。 *咧嘴*对不起:来回答中有一些有趣的细节。如果我正确地理解了代码,就会创建一个动态SUBCLASS,它应该仍然保持原始异常接口与外部世界的接口。即//增加A;重新B(A); catch A:也应该抓住B的实例。光滑! – James 2011-05-07 20:41:08

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 

我不喜欢这种非常多,虽然。为什么你想要做这样的事情呢?

+0

我在500KSLOC程序中有一个较老的类,它埋在应用程序的深处,并抛出处理高得多的异常。我想将信息附加到该类抛出的每个异常。 – James 2011-05-07 17:32:02

+0

您的解决方案似乎可行(并且保持异常类与抛出的相同)。任何概念如何可靠地附加到参数?依赖大部分信息中某处显示参数的内容是否安全? – James 2011-05-07 20:38:32

+0

如果'e'类是'exception.BaseException'的一个子类,并且没有任何人与内部混淆,那么它应该工作。但要注意,在Python 2.x中,任何旧式样类都可以作为例外引发,即使它不包含内置的异常类。那些可能不会有'args'属性。 – pillmuncher 2011-05-07 20:57:03