Дамп подкласса gtk.ListStore с использованием pickle - PullRequest
4 голосов
/ 11 мая 2011

Я пытаюсь сбросить пользовательский класс с помощью 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».Используя операторы print, я убедился, что функция __getstate__ запускается при использовании pickle.dump.Я не вижу никаких подсказок относительно того, что делать дальше из документации, и поэтому я немного затруднен.Есть предложения?

Ответы [ 2 ]

1 голос
/ 11 мая 2011

С помощью этого метода вы даже можете использовать json вместо pickle для своих целей.

Вот быстрый рабочий пример, демонстрирующий шаги, которые необходимо выполнить, чтобы выбрать «необратимые типы», такие как gtk.ListStore.По сути, вам нужно сделать несколько вещей:

  1. Определить __reduce__, который возвращает функцию и аргументы, необходимые для восстановления экземпляра.
  2. Определите типы столбцов для вашего ListStore.Метод self.get_column_type(0) возвращает Gtype, поэтому вам необходимо отобразить его обратно на соответствующий тип Python.Я оставил это как упражнение - в моем примере я использовал хак для получения типов столбцов из первого ряда значений.
  3. Ваша функция _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]
0 голосов
/ 11 мая 2011

Когда вы создаете подкласс для объекта, 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.

...