python:如何使用多个init args pickleable创建自定义异常类

How to make a custom exception class with multiple init args pickleable

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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)

此代码抛出以下错误:

1
2
3
4
5
6
7
8
9
10
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编辑:清理标题/内容


如果您同时使用两个参数来构造传递给父异常类的错误消息,则当前答案将被分解。我认为最好的方法是在异常中简单地覆盖__reduce__方法。__reduce__方法应该返回一个两项元组。元组中的第一项是类。第二项是一个元组,包含传递给类的__init__方法的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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__的信息请点击这里。


arg2可选:

1
2
3
4
5
class MyException(Exception):
    def __init__(self, arg1, arg2=None):
        self.arg1 = arg1
        self.arg2 = arg2
        super(MyException, self).__init__(arg1)

Exception类定义了一个.__reduce__()方法,使扩展(基于c)类型picklable,该方法只需要一个参数(即.args);参见C源代码中的BaseException_reduce()函数。

最简单的解决方法是让额外的参数可选。__reduce__方法还包括除.args.message之外的任何其他对象属性,您的实例将被正确地重新创建:

1
2
3
4
5
6
7
8
9
10
>>> 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'


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

1
2
3
4
5
class MyException(Exception):
    def __init__(self, arg1, arg2):
        super(MyException, self).__init__(arg1, arg2)        
        self.arg1 = arg1
        self.arg2 = arg2

基本的Exception类的__reduce__方法将包含所有的arg。通过不让所有额外的参数都是可选的,您可以确保异常构造正确。