Дамп подкласса gtk.ListStore с использованием pickle
Я пытаюсь сбросить пользовательский класс, используя рассол. Класс был разделен на подклассы из gtk.ListStore, так как это облегчало сохранение определенных данных и затем отображало их с помощью gtk. Это может быть воспроизведено, как показано здесь.
import gtk
import pickle
import os
class foo(gtk.ListStore):
pass
if __name__=='__main__':
x = foo(str)
with open(os.path.expandvars('%userprofile%\\temp.txt'),'w') as f:
pickle.dump(x,f)
Решение, которое я попробовал, состояло в том, чтобы добавить __getstate__
функция в моем классе. Насколько я понимаю документация, это должно иметь приоритет для pickle, чтобы он больше не пытался сериализовать ListStore, что он не может сделать. Тем не менее, я все равно получаю ту же ошибку от pickle.dump, когда пытаюсь засолить свой объект. Ошибка может быть воспроизведена следующим образом.
import gtk
import pickle
import os
class foo(gtk.ListStore):
def __getstate__(self):
return 'bar'
if __name__=='__main__':
x = foo(str)
with open(os.path.expandvars('%userprofile%\\temp.txt'),'w') as f:
pickle.dump(x,f)
В каждом случае pickle.dump вызывает ошибку TypeError, "не может засечь объекты ListStore". Используя печатные заявления, я убедился, что __getstate__
Функция запускается при использовании pickle.dump. Я не вижу никаких подсказок относительно того, что делать дальше из документации, и поэтому я немного затруднен. Какие-либо предложения?
2 ответа
С помощью этого метода вы можете даже использовать JSON вместо Pickle для ваших целей.
Вот быстрый рабочий пример, чтобы показать вам шаги, которые вам нужно использовать, чтобы выбрать такие "непопулярные типы", как gtk.ListStore
, По сути, вам нужно сделать несколько вещей:
- определять
__reduce__
который возвращает функцию и аргументы, необходимые для восстановления экземпляра. - Определите типы столбцов для вашего ListStore. Метод
self.get_column_type(0)
возвращает Gtype, поэтому вам нужно отобразить его обратно на соответствующий тип Python. Я оставил это как упражнение - в моем примере я использовал хак для получения типов столбцов из первого ряда значений. - Ваш
_new_foo
Функция должна будет восстановить экземпляр.
Пример:
import gtk, os, pickle
def _new_foo(cls, coltypes, rows):
inst = cls.__new__(cls)
inst.__init__(*coltypes)
for row in rows:
inst.append(row)
return inst
class foo(gtk.ListStore):
def __reduce__(self):
rows = [list(row) for row in self]
# hack - to be correct you'll really need to use
# `self.get_column_type` and map it back to Python's
# corresponding type.
coltypes = [type(c) for c in rows[0]]
return _new_foo, (self.__class__, coltypes, rows)
x = foo(str, int)
x.append(['foo', 1])
x.append(['bar', 2])
s = pickle.dumps(x)
y = pickle.loads(s)
print list(y[0])
print list(y[1])
Выход:
['foo', 1]
['bar', 2]
Когда вы подкласса объекта, object.__reduce__
заботится о звонке __getstate__
, Казалось бы, так как это подкласс gtk.ListStore
реализация по умолчанию __reduce__
пытается мариновать данные для восстановления gtk.ListStore
сначала объект, а затем вызывает ваш __getstate__
, но так как gtk.ListStore
нельзя мариновать, он отказывается мариновать ваш класс. Проблема должна исчезнуть, если вы попытаетесь реализовать __reduce__
а также __reduce_ex__
вместо __getstate__
,
>>> class Foo(gtk.ListStore):
... def __init__(self, *args):
... super(Foo, self).__init__(*args)
... self._args = args
... def __reduce_ex__(self, proto=None):
... return type(self), self._args, self.__getstate__()
... def __getstate__(self):
... return 'foo'
... def __setstate__(self, state):
... print state
...
>>> x = Foo(str)
>>> pickle.loads(pickle.dumps(x))
foo
<Foo object at 0x18be1e0 (__main__+Foo-v3 at 0x194bd90)>
Кроме того, вы можете попробовать рассмотреть другие сериализаторы, такие как json
, Там вы получаете полный контроль над процессом serialiazaton, определяя, как пользовательские классы должны быть сериализованы самостоятельно. Плюс по умолчанию они поставляются без проблем безопасности pickle
,