2013-04-26 2 views
10

Почему мой пользовательский класс исключений ниже не сериализуется/неэтериализован правильно, используя модуль рассола?Как создать собственный класс исключений с несколькими init args pickleable

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.

Спасибо за любую помощь. Кайл

EDIT: Закрепление мой призыв к супер в shx2 EDIT: Очистка Название/содержание

ответ

7

Сделать arg2 дополнительно:

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

Основание Exception класс определяет .__reduce__() method сделать расширение (C), и этот метод ожидает только один аргумент (который равен .args); см. BaseException_reduce() function in the C source.

Простейший обход делает дополнительные аргументы необязательными. Метод __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' 
7

Мне нравится ответ Мартейн, но я думаю, что лучший способ, чтобы передать все аргументы в Exception базового класса :

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

__reduce__ метод базового класса Exception будет включать в себя все арг. Не сделав все дополнительные аргументы необязательными, вы можете убедиться, что исключение построено правильно.

4

Текущие ответы ломаются, если вы используете оба аргумента для создания сообщения об ошибке для перехода к родительскому классу исключений. Я считаю, что лучший способ - просто переопределить метод __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__here.