The BaseException
class defines a custom __reduce__
method in exceptions.c, which returns the list of arguments to pass to __init__
. Exact code is
if (self->args && self->dict)
return PyTuple_Pack(3, Py_TYPE(self), self->args, self->dict);
else
return PyTuple_Pack(2, Py_TYPE(self), self->args);
According to __reduce__
documentation,
- the first item of the tuple is the callable to invoke. Here, that will be the exception class.
- the second item is the tuple of arguments to pass to the callable. Here, that will be
self.args
.
- the third item is a dict to merge into
self.__dict__
.
So from this, BaseException.__reduce__
will make unpickle invoke the exception's constructor with given args.
You have two options: either override __reduce__
, or put the required arguments in self.args, either directly or by letting the parent class do it:
import pickle
class ABError(Exception):
def __init__(self, a, b):
self.a = a
self.b = b
# self.args = (a, b)
# maybe better, let base class's __init__ do it =>
super(ABError, self).__init__(a, b)
ab_err = ABError("aaaa", "bbbb")
pickled = pickle.dumps(ab_err)
original = pickle.loads(pickled) # no longer fails
Note that the original issue comes from the rather naive way BaseException
pickle handling works. It is fixed in the latest python3 releases. Your question's original code works fine on python 3.5 for instance.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…