Как создать собственный класс исключений с несколькими 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.
Спасибо за любую помощь.
Кайл
РЕДАКТИРОВАТЬ: Фиксация моего звонка на супер за shx2
EDIT: очистка заголовка/содержимого
Ответы
Ответ 1
Сделайте arg2
необязательным:
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
); см. функцию 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'
Ответ 2
Мне нравится ответ Martijn, но я думаю, что лучший способ - передать все аргументы базовому классу Exception
:
class MyException(Exception):
def __init__(self, arg1, arg2):
super(MyException, self).__init__(arg1, arg2)
self.arg1 = arg1
self.arg2 = arg2
В базовый метод Exception
class '__reduce__
будут включены все аргументы. Не добавляя дополнительные аргументы необязательно, вы можете убедиться, что исключение построено правильно.
Ответ 3
Текущие ответы ломаются, если вы используете оба аргумента для создания сообщения об ошибке для перехода к родительскому классу исключений. Я считаю, что лучший способ - просто переопределить метод __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__
здесь.