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
In Python, users can define custom exceptions by creating a new class. This exception class has to be derived, either directly or indirectly, from the built-in Exception class. Most of the built-in exceptions are also derived from this class.
In any case, you only need __init__ or __str__ if you do something different from what Exception itself does.
The most common method to catch and print the exception message in Python is by using except and try statement. You can also save its error message using this method. Another method is to use logger.
An exception is an event, which occurs during the execution of a program that disrupts the normal flow of the program's instructions. In general, when a Python script encounters a situation that it cannot cope with, it raises an exception. An exception is a Python object that represents an error.
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.
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'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With