在Python中优雅地降级酸洗

时间:2009-08-28 17:13:40

标签: python pickle graceful-degradation

(您可能会在某些背景下阅读this问题)

我希望有一种优雅降级的方法来在Python中腌制对象。

当腌制一个对象时,让我们把它称为主对象,有时候Pickler会引发一个异常,因为它无法腌制主对象的某个子对象。例如,我经常遇到的错误是“无法腌制模块对象”。那是因为我从主要对象引用了一个模块。

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

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

有这样的事吗?有没有人知道如何处理这个问题?


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

3 个答案:

答案 0 :(得分:3)

您可以决定并实现以前不可解决的类型如何被pickle和unpickled:请参阅标准库模块copy_reg(在Python 3中重命名为copyreg。*)。

本质上,你需要提供一个函数,给定一个类型的实例,将它减少为一个元组 - 使用与reduce特殊方法相同的协议(除了reduce特殊方法不带参数,因为当它被提供时,它直接在对象上调用,而你提供的函数将把对象作为唯一的参数。)

通常,您返回的元组有两个项目:一个可调用的,以及一个传递给它的参数元组。必须将callable注册为“安全构造函数”,或者等效地具有带有true值的属性__safe_for_unpickling__。这些项目将被pickle,并且在unpickling时,将使用给定的参数调用callable,并且必须返回未被删除的对象。

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

>>> 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调用内置的import函数,使用字符串{ {1}}作为唯一的论点。并且sys表明,根据需要,这确实会让我们回到内置的z模块作为unpickling的结果。

现在,你必须让事情变得比sys更复杂(你必须处理保存和恢复动态变化,导航嵌套命名空间等),因此你将会在__import__返回你的其他函数的模块保存函数之前,还必须调用copy_reg.constructor(作为参数传递你自己的执行此工作的函数)(如果在另一个运行中,也在你之前)使用所述功能取消你制作的泡菜)。但是我希望这个简单的案例有助于表明实际上并没有什么东西可以“本质上”复杂化! - )

答案 1 :(得分: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)

答案 2 :(得分: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类的扩展......