2011-05-06 79 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