Почему возникает ошибка в моем классе, определяющем __slots__ при попытке рассортировать объект?
Я пытаюсь разжечь объект класса (нового стиля), который я определил. Но я получаю следующую ошибку:
>>> with open('temp/connection.pickle','w') as f:
... pickle.dump(c,f)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "/usr/lib/python2.5/pickle.py", line 1362, in dump
Pickler(file, protocol).dump(obj)
File "/usr/lib/python2.5/pickle.py", line 224, in dump
self.save(obj)
File "/usr/lib/python2.5/pickle.py", line 331, in save
self.save_reduce(obj=obj, *rv)
File "/usr/lib/python2.5/pickle.py", line 419, in save_reduce
save(state)
File "/usr/lib/python2.5/pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "/usr/lib/python2.5/pickle.py", line 649, in save_dict
self._batch_setitems(obj.iteritems())
File "/usr/lib/python2.5/pickle.py", line 663, in _batch_setitems
save(v)
File "/usr/lib/python2.5/pickle.py", line 306, in save
rv = reduce(self.proto)
File "/usr/lib/python2.5/copy_reg.py", line 76, in _reduce_ex
raise TypeError("a class that defines __slots__ without "
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
Я явно не определил __slots__
в своем классе. Что-то я неявно определяю? Как мне обойти это? Нужно ли определять __getstate__
?
Обновление: gnibbler выбрало хороший пример. Класс объекта, который я пытаюсь раскрыть, обертывает сокет. (Теперь мне кажется, что) сокеты определяют __slots__
, а не __getstate__
по уважительной причине. Я предполагаю, что, как только процесс завершится, другой процесс не может распаковываться и использовать предыдущее соединение сокетов процесса. Поэтому, в то время как я признаю Alex Martelli отличный ответ, мне придется проводить другую стратегию, чем травление, чтобы "поделиться" ссылкой на объект.
Ответы
Ответ 1
Класс, определяющий __slots__
(а не __getstate__
) может быть либо классом-предком, либо классом (или классом предка) вашего атрибута или вашего элемента, прямо или косвенно: по существу, класс любого объекта в ориентированном графе ссылок с вашим объектом как root, так как травление нужно сохранить весь график.
Простым решением вашего затруднительного положения является использование протокола -1
, что означает, что "наилучший протокол может использовать протокол"; по умолчанию используется древний протокол на основе ASCII, который накладывает это ограничение на __slots__
vs __getstate__
. Рассмотрим:
>>> class sic(object):
... __slots__ = 'a', 'b'
...
>>> import pickle
>>> pickle.dumps(sic(), -1)
'\x80\x02c__main__\nsic\nq\x00)\x81q\x01.'
>>> pickle.dumps(sic())
Traceback (most recent call last):
[snip snip]
raise TypeError("a class that defines __slots__ without "
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
>>>
Как вы видите, протокол -1
принимает шаг __slots__
в шаге, в то время как протокол по умолчанию дает то же исключение, которое вы видели.
Проблемы с протоколом -1
: он создает двоичную строку/файл, а не ASCII, такой как протокол по умолчанию; полученный маринованный файл не будет загружаться достаточно древними версиями Python. К преимуществам, помимо ключевого параметра wrt __slots__
, относятся более компактные результаты и более высокая производительность.
Если вы вынуждены использовать протокол по умолчанию, вам нужно будет точно определить, какой класс дает вам проблемы и почему именно. Мы можем обсудить стратегии, если это так (но если вы можете использовать протокол -1
, что намного лучше, чем это не стоит обсуждать;-) и простая проверка кода, ищущая неприятный класс/объект, оказывается слишком сложной ( Я имею в виду некоторые трюки, основанные на глубокой топике, чтобы получить полезное представление всего графика, если вам интересно).
Ответ 2
Возможно, атрибут вашего экземпляра использует __slots__
Например, socket
имеет __slots__
, поэтому его нельзя травить
Вам нужно определить, какой атрибут вызывает ошибку, и написать свой собственный
__getstate__
и __setstate__
, чтобы игнорировать этот атрибут
Ответ 3
От PEP 307:
Метод __getstate__
должен возвращать значение picklable представляющий состояние объекта без ссылки на объект сам. Если метод __getstate__
не существует, значение по умолчанию используется реализация, которая возвращает self.__dict__
.