简体   繁体   English

使用pickle转储gtk.ListStore的子类

[英]Dumping a subclass of gtk.ListStore using pickle

I am trying to dump a custom class using pickle. 我正在尝试使用pickle转储自定义类。 The class was subclassed from gtk.ListStore, since that made it easier to store particular data and then display it using gtk. 该类是从gtk.ListStore子类化的,因为这样可以更容易地存储特定数据,然后使用gtk显示它。 This can be reproduced as shown here. 这可以如此处所示再现。

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)

The solution that I have tried was to add a __getstate__ function into my class. 我尝试过的解决方案是在我的类中添加一个__getstate__函数。 As far as I understand the documentation , this should take precedence for pickle so that it no longer tries to serialize the ListStore, which it is unable to do. 据我理解文档 ,这应该优先于pickle,以便它不再尝试序列化ListStore,它无法做到。 However, I still get an identical error from pickle.dump when I try to pickle my object. 但是,当我试图挑选我的对象时,我仍然从pickle.dump得到一个相同的错误。 The error can be reproduced as follows. 该错误可以如下再现。

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)

In each case, pickle.dump raises a TypeError, "can't pickle ListStore objects". 在每种情况下,pickle.dump引发一个TypeError,“不能pickle ListStore对象”。 Using print statements, I have verified that the __getstate__ function is run when using pickle.dump. 使用print语句,我已经验证了使用pickle.dump时运行了__getstate__函数。 I don't see any hints as to what to do next from the documentation, and so I'm in a bit of a bind. 我没有看到关于文档中接下来要做什么的任何提示,所以我有点绑定。 Any suggestions? 有什么建议?

With this method you can even use json instead of pickle for your purpose. 使用这种方法,您甚至可以使用json代替pickle。

Here is a quick working example to show you the steps you need to employ to pickle "unpicklable types" like gtk.ListStore . 这是一个快速工作的示例,向您展示了需要采用的步骤来挑选像gtk.ListStore这样的“无法解决的类型”。 Essentially you need to do a few things: 基本上你需要做一些事情:

  1. Define __reduce__ which returns a function and arguments needed to reconstruct the instance. 定义__reduce__ ,它返回重建实例所需的函数和参数。
  2. Determine the column types for your ListStore. 确定ListStore的列类型。 The method self.get_column_type(0) returns a Gtype, so you will need to map this back to the corresponding Python type. self.get_column_type(0)方法返回一个Gtype,因此您需要将其映射回相应的Python类型。 I've left that as an exercise - in my example I've employed a hack to get the column types from the first row of values. 我把它留作练习 - 在我的例子中,我使用了一个hack从第一行的值中获取列类型。
  3. Your _new_foo function will need to rebuild the instance. 您的_new_foo函数需要重建实例。

Example: 例:

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])

Output: 输出:

['foo', 1]
['bar', 2]

When you subclass object, object.__reduce__ takes care of calling __getstate__ . 当你object.__reduce__ object时, object.__reduce__负责调用__getstate__ It would seem that since this is a subclass of gtk.ListStore , the default implementation of __reduce__ tries to pickle the data for reconstructing a gtk.ListStore object first, then calls your __getstate__ , but since the gtk.ListStore can't be pickled, it refuses to pickle your class. 看来因为这是gtk.ListStore的子类, gtk.ListStore的默认实现__reduce__尝试pickle数据重建gtk.ListStore对象,然后调用__getstate__ ,但由于gtk.ListStore不能被pickle,它拒绝腌你的班级。 The problem should go away if you try to implement __reduce__ and __reduce_ex__ instead of __getstate__ . 如果你尝试实现这个问题应该消失__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)>

As an addition, you may try to consider other serializers, such as json . 另外,您可以尝试考虑其他序列化程序,例如json There you take full control of the serialiazaton process by defining how custom classes are to be serialized yourself. 在那里,您可以通过定义自定义类的自定义类来完全控制serialiazaton进程。 Plus by default they come without the security issues of pickle . 另外,默认情况下,他们没有pickle的安全问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM