2011-05-11 46 views
4

我想使用pickle转储一个自定义类。该类从gtk.ListStore继承而来,因为这使得更容易存储特定数据,然后使用gtk显示它。这可以如下所示进行再现。使用pickle转储gtk.ListStore的子类

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, “不能酸洗ListStore对象”。使用print语句,我已经验证了__getstate__函数在使用pickle.dump时运行。我从文档中看不到有关下一步要做什么的提示,所以我有点困惑。有什么建议么?

回答

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

1

使用此方法,您甚至可以使用json而不是pickle来达到您的目的。

下面是一个快速工作示例,向您展示您需要采用的步骤来腌制“不可抽出类型”,如gtk.ListStore。基本上你需要做一些事情:

  1. 定义__reduce__它返回一个函数和参数需要重建实例。
  2. 确定ListStore的列类型。方法self.get_column_type(0)返回一个Gtype,因此您需要将其映射回相应的Python类型。我已经把它作为一个练习了 - 在我的例子中,我使用了一个hack从第一行值中获取列类型。
  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]