(您可能会在某些背景下阅读this问题)
我希望有一种优雅降级的方法来在Python中腌制对象。
当腌制一个对象时,让我们把它称为主对象,有时候Pickler会引发一个异常,因为它无法腌制主对象的某个子对象。例如,我经常遇到的错误是“无法腌制模块对象”。那是因为我从主要对象引用了一个模块。
我知道我可以写一些东西用一个包含模块属性的外观替换该模块,但这会产生自己的问题(1)。
所以我想要的是一个酸洗功能,可以自动用包含其属性的外墙替换模块(以及任何其他难以腌制的对象)。这可能不会产生完美的酸洗,但在许多情况下它就足够了。
有这样的事吗?有没有人知道如何处理这个问题?
(1)一个问题是该模块可能正在引用其中的其他模块。
答案 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类的扩展......