Солить все атрибуты, кроме одного
Каков наилучший способ написать метод __getstate__
, который солит почти все атрибуты объекта, но исключает несколько?
У меня есть объект со многими свойствами, включая тот, который ссылается на метод instance. instancemethod не разборчивы, поэтому я получаю сообщение об ошибке при попытке рассортировать этот объект:
class Foo(object):
def __init__(self):
self.a = 'spam'
self.b = 'eggs'
self.c = 42
self.fn = self.my_func
def my_func(self):
print 'My hovercraft is full of eels'
import pickle
pickle.dumps(Foo()) # throws a "can't pickle instancemethod objects" TypeError
Этот метод __getstate__
исправляет это, но затем мне нужно вручную включить все свойства, которые я хочу сериализовать:
def __getstate__(self):
return { 'a': self.a, 'b': self.b, 'c': self.c }
Это не очень масштабируемое или поддерживаемое, если у меня есть объект со многими атрибутами или который изменяется часто.
Единственная альтернатива, о которой я могу думать, это некоторая вспомогательная функция, которая выполняет итерации через свойства объекта и добавляет их (или нет) в словарь, основываясь на типе.
Ответы
Ответ 1
Единственная альтернатива, о которой я могу думать, это некоторая вспомогательная функция, которая выполняет итерации через свойства объекта и добавляет их (или нет) в словарь, основываясь на типе.
Да, я думаю, что в значительной степени то, что вам осталось, если вы хотите достаточно "магии", чтобы позволить себе быть ленивым (и/или допускать динамически добавленные атрибуты). Имейте в виду, что "pickle
не может справиться с этим" - не единственная причина, по которой вы не хотите включать что-то в маринованное состояние.
Но это не так сложно, как вы, кажется, думаете, предполагая, что у вас есть код для "должен ли я это делать?" логика:
def __getstate__(self):
return dict((k, v) for (k, v) in self.__dict__.iteritems() if should_pickle(v))
Ответ 2
Используя is_instance_method
из более раннего ответа:
def __getstate__(self):
return dict((k, v) for k, v in self.__dict__.iteritems()
if not is_instance_method(getattr(self, k)))
Хотя операция is_instance_method
также может выполняться менее "магически", используя метод известного экземпляра, скажем my_func
и принимая его тип.
def __getstate__(self):
instancemethod = type(self.my_func)
return dict((k, v) for k, v in self.__dict__.iteritems()
if not isinstance(getattr(self, k), instancemethod))
Ответ 3
Вы всегда можете просто удалить плохие элементы:
def __getstate__(self):
state = self.__dict__
del state[...]
return state
Ответ 4
Я разрезал бы корень вашей проблемы и попытался сначала сериализовать так называемые "не-pickleable" элементы.
Для этого я бы использовал dill, который может сериализовать почти что угодно в python. У Dill также есть несколько хороших инструментов, которые помогут вам понять, что заставляет ваш травление терпеть неудачу, когда ваш код не работает.
>>> import dill
>>> dill.loads(dill.dumps(your_bad_object))
>>> ...
>>> # if you get a pickling error, use dill tools to figure out a workaround
>>> dill.detect.badobjects(your_bad_object, depth=0)
>>> dill.detect.badobjects(your_bad_object, depth=1)
>>> ...
Если вы абсолютно этого хотели, вы могли бы использовать dill badobjects
(или одну из других функций обнаружения) для рекурсивного погружения в цепочку ссылок объектов и выталкивать нераспадаемые объекты вместо того, чтобы называть их на каждой глубине, как указано выше.
Ответ 5
__slots__
решение
Если вы используете слоты, вы можете избежать повторения элементов для исключения:
class C(object):
_pickle_slots = ['i']
__slots__ = _pickle_slots + ['j']
def __init__(self, i, j):
self.i = i
self.j = j
def __getstate__(self):
return (None, {k:getattr(self, k) for k in C._pickle_slots })
o = pickle.loads(pickle.dumps(C(1, 2), -1))
# i is there
assert o.i == 1
# j was excluded
try:
o.j
except:
pass
else:
raise
Протестировано в Python 2.7.6.