Python:用一些不可打击的物品腌制一个字典

Ram*_*hum 11 python graceful-degradation pickle

我有一个gui_project具有属性的对象.namespace,这是一个命名空间字典.(即从字符串到对象的字典.)

(这在类似IDE的程序中使用,让用户在Python shell中定义自己的对象.)

我想gui_project用命名空间来腌制它.问题是,命名空间中的某些对象(即.namespacedict的值)不是可选对象.例如,其中一些是指wxPython小部件.

我想过滤掉不可剔除的对象,也就是说,将它们从pickle版本中排除.

我怎样才能做到这一点?

(我尝试过的一件事就是逐一对价值进行尝试并尝试腌制它们,但是发生了一些无限递归,我需要保护它.)

(我现在实现了一种GuiProject.__getstate__方法,除了其他无法解决的东西namespace.)

Sha*_*way 6

我会使用pickler对持久对象引用的文档支持.持久对象引用是由pickle引用但不存储在pickle中的对象.

http://docs.python.org/library/pickle.html#pickling-and-unpickling-external-objects

ZODB多年来一直使用这个API,所以非常稳定.unpickling时,您可以用您喜欢的任何内容替换对象引用.在您的情况下,您可能希望用标记替换对象引用,指示无法对对象进行pickle.

你可以从这样的事情开始(未经测试):

import cPickle

def persistent_id(obj):
    if isinstance(obj, wxObject):
        return "filtered:wxObject"
    else:
        return None

class FilteredObject:
    def __init__(self, about):
        self.about = about
    def __repr__(self):
        return 'FilteredObject(%s)' % repr(self.about)

def persistent_load(obj_id):
    if obj_id.startswith('filtered:'):
        return FilteredObject(obj_id[9:])
    else:
        raise cPickle.UnpicklingError('Invalid persistent id')

def dump_filtered(obj, file):
    p = cPickle.Pickler(file)
    p.persistent_id = persistent_id
    p.dump(obj)

def load_filtered(file)
    u = cPickle.Unpickler(file)
    u.persistent_load = persistent_load
    return u.load()
Run Code Online (Sandbox Code Playgroud)

然后只调用dump_filtered()和load_filtered()而不是pickle.dump()和pickle.load().wxPython对象将作为持久性ID进行pickle,在unpickling时将被FilteredObjects替换.

您可以通过过滤掉不属于内置类型且没有__getstate__方法的对象来使解决方案更通用.

更新(2010年11月15日):这是一种使用包装类实现相同功能的方法.使用包装类而不是子类,可以保留在记录的API中.

from cPickle import Pickler, Unpickler, UnpicklingError


class FilteredObject:
    def __init__(self, about):
        self.about = about
    def __repr__(self):
        return 'FilteredObject(%s)' % repr(self.about)


class MyPickler(object):

    def __init__(self, file, protocol=0):
        pickler = Pickler(file, protocol)
        pickler.persistent_id = self.persistent_id
        self.dump = pickler.dump
        self.clear_memo = pickler.clear_memo

    def persistent_id(self, obj):
        if not hasattr(obj, '__getstate__') and not isinstance(obj,
            (basestring, int, long, float, tuple, list, set, dict)):
            return "filtered:%s" % type(obj)
        else:
            return None


class MyUnpickler(object):

    def __init__(self, file):
        unpickler = Unpickler(file)
        unpickler.persistent_load = self.persistent_load
        self.load = unpickler.load
        self.noload = unpickler.noload

    def persistent_load(self, obj_id):
        if obj_id.startswith('filtered:'):
            return FilteredObject(obj_id[9:])
        else:
            raise UnpicklingError('Invalid persistent id')


if __name__ == '__main__':
    from cStringIO import StringIO

    class UnpickleableThing(object):
        pass

    f = StringIO()
    p = MyPickler(f)
    p.dump({'a': 1, 'b': UnpickleableThing()})

    f.seek(0)
    u = MyUnpickler(f)
    obj = u.load()
    print obj

    assert obj['a'] == 1
    assert isinstance(obj['b'], FilteredObject)
    assert obj['b'].about
Run Code Online (Sandbox Code Playgroud)


Ram*_*hum 1

我最终使用 Shane Hathaway 的方法编写了自己的解决方案。

这是代码。(查找CutePicklerCuteUnpickler。)这是测试。它是GarlicSim的一部分,因此您可以通过安装garlicsim和执行来使用它from garlicsim.general_misc import pickle_tools

如果您想在 Python 3 代码上使用它,请使用garlicsim.