问题
Why does my custom Exception class below not serialize/unserialize correctly using the pickle module?
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)
This code throws the following error:
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)
I'm sure this problem stems from a lack of knowledge on my part of how to make a class pickle-friendly. Interestingly, this problem doesn't occur when my class doesn't extend Exception.
Thanks for any help. Kyle
EDIT: Fixing my call to super per shx2 EDIT: Cleaning up title/content
回答1:
Make arg2
optional:
class MyException(Exception):
def __init__(self, arg1, arg2=None):
self.arg1 = arg1
self.arg2 = arg2
super(MyException, self).__init__(arg1)
The base Exception
class defines a .__reduce__() method to make the extension (C-based) type picklable and that method only expects one argument (which is .args
); see the BaseException_reduce() function in the C source.
The easiest work-around is making extra arguments optional. The __reduce__
method also includes any additional object attributes beyond .args
and .message
and your instances are recreated properly:
>>> 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'
回答2:
The current answers break down if you're using both arguments to construct an error message to pass to the parent Exception class. I believe the best way is to simply override the __reduce__
method in your exception. The __reduce__
method should return a two item tuple. The first item in the tuple is your class. The second item is a tuple containing the arguments to pass to your class's __init__
method.
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
More info about __reduce__
here.
回答3:
I like Martijn's answer, but I think a better way is to pass all arguments to the Exception
base class:
class MyException(Exception):
def __init__(self, arg1, arg2):
super(MyException, self).__init__(arg1, arg2)
self.arg1 = arg1
self.arg2 = arg2
The base Exception
class' __reduce__
method will include all the args. By not making all of the extra arguments optional, you can ensure that the exception is constructed correctly.
回答4:
I simply do this
class MyCustomException(Exception):
def __init__(self):
self.value = 'Message about my error'
def __str__(self):
return repr(self.value)
... somewhere in code ...
raise MyCustomException
来源:https://stackoverflow.com/questions/16244923/how-to-make-a-custom-exception-class-with-multiple-init-args-pickleable