2013-04-26 7 views
10

以下のカスタムExceptionクラスは、pickleモジュールを使用してシリアル化/シリアル化を正しく行わないのはなぜですか?複数のinit argsでカスタム例外クラスを作成する方法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) 

私はこの問題は、クラスのピクルスフレンドリーを作成する方法の私の部分に知識の欠如に起因すると確信しています。興味深いことに、この問題は、クラスが例外を拡張しない場合には発生しません。

ありがとうございました。 カイル

EDIT:arg2をオプションにタイトル/コンテンツ

答えて

7

のクリーンアップ:スーパーshx2あたり EDITに私のコールを修正

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

ベースExceptionクラスは、(拡張子を作るために.__reduce__() methodを定義しますCベース)のpicklableであり、そのメソッドはの1つだけの引数(これは.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

私はマルタインの答えを好きですが、私は良い方法はException基底クラスへのすべての引数を渡すことだと思います:

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

ベースExceptionクラスの__reduce__方法は、すべての引数が含まれます。余分な引数をすべて省略することで、例外が正しく構築されるようにすることができます。

4

親のExceptionクラスに渡すエラーメッセージを構築するために両方の引数を使用している場合、現在の応答は分解されます。あなたの例外で__reduce__メソッドをオーバーライドするのが最善の方法だと思います。 __reduce__メソッドは、2つの項目タプルを返す必要があります。タプルの最初の項目はクラスです。 2番目の項目は、クラスの__init__メソッドに渡す引数を含むタプルです。

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についての詳細情報。

関連する問題