2013-04-26 50 views
10

爲什麼我的自定義Exception類不能正確使用pickle模塊序列化/反序列化?如何使用多個初始參數pickleable來創建自定義異常類

import pickle 

class MyException(Exception): 
    def __init__(self, arg1, arg2): 
     self.arg1 = arg1 
     self.arg2 = arg2 

     super(MyException, self).__init__(arg1) 

e = MyException("foo", "bar") 

str = pickle.dumps(e) 
obj = pickle.loads(str) 

此代碼引發以下錯誤:

Traceback (most recent call last): 
File "test.py", line 13, in <module> 
    obj = pickle.loads(str) 
File "/usr/lib/python2.7/pickle.py", line 1382, in loads 
    return Unpickler(file).load() 
File "/usr/lib/python2.7/pickle.py", line 858, in load 
    dispatch[key](self) 
File "/usr/lib/python2.7/pickle.py", line 1133, in load_reduce 
    value = func(*args) 
TypeError: __init__() takes exactly 3 arguments (2 given) 

我敢肯定,從我如何使一類泡菜友好的部分知識的缺乏這一問題造成的。有趣的是,當我的類沒有擴展Exception時,這個問題不會發生。

感謝您的任何幫助。 凱爾

編輯:修我呼籲超級每shx2 編輯:清理標題/內容

回答

7

arg2可選:

class MyException(Exception): 
    def __init__(self, arg1, arg2=None): 
     self.arg1 = arg1 
     self.arg2 = arg2 
     super(MyException, self).__init__(arg1) 

基礎Exception類定義.__reduce__() method,使擴展(基於C的)類型的可揀貨物,並且該方法僅期望一個參數(其爲.args);請參閱BaseException_reduce() function in the C source

最簡單的解決方法是使額外的參數可選。該__reduce__方法包括任何額外的對象屬性超越.args.message和您的實例正確地重新創建:

>>> e = MyException('foo', 'bar') 
>>> e.__reduce__() 
(<class '__main__.MyException'>, ('foo',), {'arg1': 'foo', 'arg2': 'bar'}) 
>>> pickle.loads(pickle.dumps(e)) 
MyException('foo',) 
>>> e2 = pickle.loads(pickle.dumps(e)) 
>>> e2.arg1 
'foo' 
>>> e2.arg2 
'bar' 
7

我喜歡的Martijn的答案,但我認爲更好的辦法是將所有參數傳遞給Exception基類:

class MyException(Exception): 
    def __init__(self, arg1, arg2): 
     super(MyException, self).__init__(arg1, arg2)   
     self.arg1 = arg1 
     self.arg2 = arg2 

基部Exception__reduce__方法將包括所有的ARGS。通過不使所有額外參數可選,您可以確保異常構造正確。

4

如果您使用兩個參數來構造傳遞給父Exception類的錯誤消息,那麼當前答案會失效。我相信最好的辦法是在你的例外中簡單地覆蓋__reduce__方法。 __reduce__方法應該返回一個兩元組元組。元組中的第一項是你的類。第二項是包含傳遞給你的類的方法的元組的元組。

import pickle 

class MyException(Exception): 
    def __init__(self, arg1, arg2): 
     self.arg1 = arg1 
     self.arg2 = arg2 

     super(MyException, self).__init__('arg1: {}, arg2: {}'.format(arg1, arg2)) 

    def __reduce__(self): 
     return (MyException, (self.arg1, self.arg2)) 


original = MyException('foo', 'bar') 
print repr(original) 
print original.arg1 
print original.arg2 

reconstituted = pickle.loads(pickle.dumps(original)) 
print repr(reconstituted) 
print reconstituted.arg1 
print reconstituted.arg2 

更多信息有關__reduce__here

相關問題