6

(您可能会阅读问题以了解一些背景)

我想有一种优雅的降级方式来在 Python 中腌制对象。

当pickle一个对象时,我们称它为main object,有时Pickler会抛出一个异常,因为它不能pickle主对象的某个子对象。例如,我经常遇到的一个错误是“不能腌制模块对象”。那是因为我从主对象引用了一个模块。

我知道我可以写一些东西来用包含模块属性的外观替换该模块,但这会有其自身的问题(1)。

所以我想要的是一个酸洗功能,它会自动将模块(以及任何其他难以酸洗的对象)替换为包含其属性的外观。这可能不会产生完美的酸洗,但在许多情况下就足够了。

有这样的吗?有谁知道如何解决这个问题?


(1) 一个问题是该模块可能在其中引用其他模块。

4

3 回答 3

3

您可以决定并实现任何以前不可提取的类型如何被腌制和解封:请参阅标准库模块copy_regcopyreg在 Python 3.* 中重命名为)。

本质上,您需要提供一个函数,在给定类型实例的情况下,将其简化为一个元组——使用与 reduce 特殊方法相同的协议除了 reduce 特殊方法不带参数,因为提供时直接调用它在对象上,而您提供的函数将对象作为唯一的参数)。

通常,您返回的元组有 2 个项目:一个可调用项和一个要传递给它的参数元组。可调用对象必须注册为“安全构造函数”或等效地具有具有__safe_for_unpickling__真值的属性。这些项目将被腌制,并且在取消腌制时,将使用给定的参数调用可调用对象,并且必须返回未选中的对象。

例如,假设您只想按名称腌制模块,因此取消腌制它们只是意味着重新导入它们(即,为简单起见,假设您不关心动态修改的模块、嵌套包等,只是简单的顶级模块)。然后:

>>> import sys, pickle, copy_reg
>>> def savemodule(module):
...   return __import__, (module.__name__,)
... 
>>> copy_reg.pickle(type(sys), savemodule)
>>> s = pickle.dumps(sys)
>>> s
"c__builtin__\n__import__\np0\n(S'sys'\np1\ntp2\nRp3\n."
>>> z = pickle.loads(s)
>>> z
<module 'sys' (built-in)>

我使用的是老式的 ASCII 形式的 pickle,因此s包含 pickle 的字符串很容易检查:它指示 unpickling 调用内置的导入函数,并将字符串sys作为其唯一参数。并且z表明这确实根据需要将内置sys模块作为 unpickling 的结果返回给我们。

现在,您必须让事情变得更复杂一些__import__(您必须处理保存和恢复动态更改、导航嵌套命名空间等),因此您还必须调用copy_reg.constructor(作为参数传递你自己的函数来执行这项工作)在你copy_reg返回你的其他函数的模块保存函数之前(并且,如果在单独的运行中,也在你解开你使用所述函数制作的那些泡菜之前)。但我希望这个简单的案例有助于表明它真的没有什么“本质上”复杂的!-)

于 2009-08-28T20:41:58.903 回答
0

下面的内容怎么样,它是一个包装器,您可以使用它来将一些模块(可能是任何模块)包装在可以腌制的东西中。然后,您可以对 Pickler 对象进行子类化,以检查目标对象是否为模块,如果是,则将其包装。这是否实现了您的愿望?

class PickleableModuleWrapper(object):
    def __init__(self, module):
        # make a copy of the module's namespace in this instance
        self.__dict__ = dict(module.__dict__)
        # remove anything that's going to give us trouble during pickling
        self.remove_unpickleable_attributes()

    def remove_unpickleable_attributes(self):
        for name, value in self.__dict__.items():
            try:
                pickle.dumps(value)
            except Exception:
                del self.__dict__[name]

import pickle
p = pickle.dumps(PickleableModuleWrapper(pickle))
wrapped_mod = pickle.loads(p)
于 2009-08-28T19:13:53.337 回答
0

嗯,有这样的吗?

import sys

attribList = dir(someobject)
for attrib in attribList:
    if(type(attrib) == type(sys)): #is a module
        #put in a facade, either recursively list the module and do the same thing, or just put in something like str('modulename_module')
    else:
        #proceed with normal pickle

显然,这将使用重新实现的转储方法进入 pickle 类的扩展......

于 2009-08-28T19:14:32.477 回答