Python Pickling Slots Ошибка

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

  File "/usr/lib/python2.6/copy_reg.py", line 77, in _reduce_ex
    raise TypeError("a class that defines __slots__ without "
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled

Я не понимаю эту ошибку, так как все мои классы, кажется, определяют метод __getstate__, и ни один, кажется, не определяет __slots__. У меня проблемы с выделением изменений, которые я сделал, вызвав эту ошибку.

Я могу только предположить, что какой-то объект, вложенный глубоко внутри моего экземпляра, вызывает это. Есть ли способ получить больше информации? Как мне найти класс точного объекта, который вызывает эту ошибку?

Ответ 1

Используйте бинарный протокол для вашего выбора (вместо старого ASCII, который вы по умолчанию используете), и все будет в порядке. Обратите внимание:

>>> class ws(object):
...   __slots__ = 'a', 'b'
...   def __init__(self, a=23, b=45): self.a, self.b = a, b
... 
>>> x = ws()
>>> import pickle
>>> pickle.dumps(x, -1)
'\x80\x02c__main__\nws\nq\x00)\x81q\x01N}q\x02(U\x01aq\x03K\x17U\x01bq\x04K-u\x86q\x05b.'
>>> pickle.dumps(x)
Traceback (most recent call last):
    [[snip]]
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/copy_reg.py", line 77, in _reduce_ex
    raise TypeError("a class that defines __slots__ without "
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
>>> 

Как видите, протокол -1 (что означает "самый лучший, самый быстрый и самый компактный протокол") работает просто отлично, в то время как протокол по умолчанию 0 (старый протокол ascii, разработанный для обеспечения совместимости на всех этапах назад до Python 1.5 и более ранних версий) - это именно то исключение, которое вы наблюдали.

Кроме того, -1 будет быстрее и даст более компактные результаты - вам просто нужно убедиться, что вы правильно сохраняете и восстанавливаете двоичные строки, которые он генерирует (например, если вы выбираете файл, обязательно откройте последний для wb, а не только для w).

Если по какой-либо причине это универсальное решение не доступно для вас, есть хаки и приемы (например, подкласс pickle.Pickler, используйте непосредственно экземпляр вашего подкласса, а не базовый, как это делает pickle.dumps), переопределите save, так что он отслеживает type(obj) перед делегированием суперклассу), но при обновлении до самого современного, самого современного протокола (-1 гарантированно будет в любой данной версии Python самым продвинутым). эта версия поддерживает) было бы хорошей идеей в любом случае, если это вообще возможно.

Ответ 2

У меня была и эта проблема, но с некоторыми уже засоленными данными в старом протоколе ASCII. Вы можете использовать эти методы для адаптации слота к вашему объекту:

class MyAlreadyPickeldObjectWithslots(object):
    ___slots__= ("attr1","attr2",....)
    def __getstate__(self):
        return dict([(k, getattr(self,k,None)) for k in self.__slots__])

    def __setstate__(self,data):
        for k,v in data.items():
            setattr(self,k,v)

Это может быть полезно, чтобы получить объем оперативной памяти