Подтвердить что ты не робот

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

4b9b3361

Ответ 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__.