Класс, определяющий __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
, это намного лучше, так что обсуждать его не стоит ;-) и простая проверка кода в поисках проблемного класса / объекта оказывается слишком сложной ( Я имею в виду некоторые трюки, основанные на глубоких копиях, чтобы получить полезное представление всего графика, если вам интересно).