Почему я получаю сообщение об ошибке определения моего класса __slots__ при попытке выбора объекта? - PullRequest
28 голосов
/ 05 февраля 2010

Я пытаюсь выбрать объект (нового стиля), который я определил. Но я получаю следующую ошибку:

>>> 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__ по уважительной причине. Я предполагаю, что как только процесс завершается, другой процесс не может открепить и использовать соединение с сокетом предыдущего процесса. Поэтому, пока я принимаю Алекс Мартелли отличный ответ, мне придется придерживаться иной стратегии, чем травление, чтобы «поделиться» ссылкой на объект.

Ответы [ 3 ]

29 голосов
/ 05 февраля 2010

Класс, определяющий __slots__ (а не __getstate__), может быть либо вашим классом-предком, либо классом (или классом-предком) атрибута или элемента прямо или косвенно: по существу, класс любого объекта в ориентированном графе ссылок с вашим объектом в качестве корневого, так как при травлении необходимо сохранить весь граф.

Простым решением проблемы является использование протокола -1, что означает «лучший протокол, который может использовать протокол»; по умолчанию используется древний протокол на основе ASCII, который накладывает это ограничение на __slots__ против __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. Преимущества, помимо ключевого в __slots__, включают более компактные результаты и лучшую производительность.

Если вы вынуждены использовать протокол по умолчанию, тогда вам необходимо точно определить, какой класс доставляет вам проблемы и почему. Мы можем обсудить стратегии, если это так (но если вы можете использовать протокол -1, это намного лучше, так что обсуждать его не стоит ;-) и простая проверка кода в поисках проблемного класса / объекта оказывается слишком сложной ( Я имею в виду некоторые трюки, основанные на глубоких копиях, чтобы получить полезное представление всего графика, если вам интересно).

6 голосов
/ 05 февраля 2010

Возможно, атрибут вашего экземпляра использует __slots__

Например, socket имеет __slots__, поэтому его нельзя мариновать

Вам необходимо определить, какой атрибут вызывает ошибку, и написать свой __getstate__ и __setstate__, чтобы игнорировать этот атрибут

2 голосов
/ 05 февраля 2010

С PEP 307 :

Метод __getstate__ должен возвращать извлекаемое значение представление состояния объекта без ссылки на объект сам. Если метод __getstate__ не существует, по умолчанию используется реализация, которая возвращает self.__dict__.

...