使用pickle转储gtk.ListStore的子类

时间:2011-05-11 17:29:00

标签: python gtk pickle getstate

我正在尝试使用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__函数。据我所知documentation,这应该优先于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,“无法pickle ListStore对象”。使用print语句,我已经验证了使用pickle.dump时运行__getstate__函数。我没有看到关于文档中接下来要做什么的任何提示,所以我有点绑定。有什么建议吗?

2 个答案:

答案 0 :(得分:1)

使用这种方法,您甚至可以使用json代替pickle。

这是一个快速工作的示例,向您展示您需要采用的步骤来挑选像gtk.ListStore这样的“无法解决的类型”。基本上你需要做一些事情:

  1. 定义__reduce__,它返回重建实例所需的函数和参数。
  2. 确定ListStore的列类型。方法self.get_column_type(0)返回一个Gtype,因此您需要将其映射回相应的Python类型。我把它留作练习 - 在我的例子中,我使用了一个黑客从第一行的值中获取列类型。
  3. 您的_new_foo函数需要重建实例。
  4. 示例:

    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]
    

答案 1 :(得分:0)

当您继承对象时,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的安全问题。