Why is my custom exception unpickle failing 的简化版本.
我正在尝试挑选一个“简单”的异常子类。它可以腌制,但在去除腌制时它会掉落:
import pickle
class ABError(Exception):
def __init__(self, a, b):
self.a = a
self.b = b
ab_err = ABError("aaaa", "bbbb")
pickled = pickle.dumps(ab_err)
original = pickle.loads(pickled) # Fails
错误:
Traceback (most recent call last):
File "p.py", line 12, in <module>
original = pickle.loads(pickled) # Fails
File "/usr/lib/python2.7/pickle.py", line 1388, in loads
return Unpickler(file).load()
File "/usr/lib/python2.7/pickle.py", line 864, in load
dispatch[key](self)
File "/usr/lib/python2.7/pickle.py", line 1139, in load_reduce
value = func(*args)
TypeError: __init__() takes exactly 3 arguments (1 given)
较早的评论表明该问题是因为内置的 Exception
类提供了 __setstate_()
方法。但是,我不清楚这是否是预期的行为 - 这看起来确实令人惊讶,因为对 object
的子类做同样的事情可以正常工作。
最佳答案
BaseException
类定义了一个自定义 __reduce__
exceptions.c 中的方法,它返回要传递给 __init__
的参数列表。确切的代码是
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);
根据__reduce__
文档,
- 元组的第一项是要调用的可调用对象。在这里,这将是异常类。
- 第二项是传递给可调用对象的参数元组。在这里,它将是
self.args
。 - 第三项是要合并到
self.__dict__
中的字典。
因此,BaseException.__reduce__
将使 unpickle 使用给定的参数调用异常的构造函数。
您有两个选择:要么覆盖 __reduce__
,要么将所需的参数直接放入 self.args 中,或者让父类这样做:
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
请注意,最初的问题来自于 BaseException
pickle 处理的相当幼稚的方式。它在最新的 python3 版本中得到修复。例如,您问题的原始代码在 python 3.5 上运行良好。
关于python - 无法解开异常子类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41808912/