42

为什么我下面的自定义异常类没有使用 pickle 模块正确序列化/反序列化?

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)

我确信这个问题源于我对如何使课程对泡菜友好的知识缺乏。有趣的是,当我的类没有扩展 Exception 时,不会出现这个问题。

谢谢你的帮助。凯尔

编辑:修复我对超级每个 shx2 的调用编辑:清理标题/内容

4

5 回答 5

43

如果您同时使用这两个参数来构造错误消息以传递给父 Exception 类,那么当前的答案就会崩溃。我相信最好的方法是简单地覆盖__reduce__您的异常中的方法。该__reduce__方法应返回一个包含两项的元组。元组中的第一项是您的班级。第二项是一个元组,其中包含要传递给类__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__ 关于这里的更多信息。

于 2016-03-31T19:57:59.707 回答
40

设为arg2可选:

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

Exception类定义了一种.__reduce__()方法来使扩展(基于 C 的)类型可挑选,并且该方法只需要一个参数(即.args);请参阅BaseException_reduce()C 源代码中的函数

最简单的解决方法是将额外的参数设为可选。该__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'
于 2013-04-26T21:02:58.170 回答
16

我喜欢 Martijn 的回答,但我认为更好的方法是将所有参数传递给Exception基类:

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

Exception类的__reduce__方法将包括所有参数。通过不将所有额外参数设为可选,您可以确保正确构造异常。

于 2014-12-06T16:51:36.167 回答
1

我只是这样做

class MyCustomException(Exception):
    def __init__(self):
        self.value = 'Message about my error'

    def __str__(self):
        return repr(self.value)

... somewhere in code ...
raise MyCustomException
于 2019-09-14T17:33:08.947 回答
0

我能够使用仅限关键字的参数获得一个自定义异常,以便像这样使用酸洗:

class CustomException(Exception):
    def __init__(self, arg1, *, kwonly):
        super().__init__(arg1, kwonly) # makes self.args == (arg1, kwonly)
        self.arg1 = arg1
        self.kwonly = kwonly

    def __str__(self):
        # Logic here to turn the args into the real exception message
        return f"{self.arg1} ({self.kwonly})"

    # __repr__ and __eq__ are not needed but are nice for testing
    def __repr__(self):
        return f"{self.__class__.__name__}({self.arg1!r}, kwonly={self.kwonly!r})"

    def __eq__(self, other):
        return isinstance(other, CustomException) and self.args == other.args

    # This is what makes pickling work
    @classmethod
    def _new(cls, arg1, kwonly):
        return cls(arg1, kwonly=kwonly)

    def __reduce__(self):
        return (self._new, (self.arg1, self.kwonly))

例子:

import pickle

w = CustomException('arg1', kwonly='kwonly')
w2 = pickle.loads(pickle.dumps(w))
assert w == w2

顺便说一句,这也适用于自定义警告类,因为它们也是Exception子类。

于 2022-02-09T21:53:29.260 回答