【问题标题】:Gracefully-degrading pickling in PythonPython中的优雅降级酸洗
【发布时间】:2010-11-23 19:33:34
【问题描述】:

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

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

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

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

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

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


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

【问题讨论】:

  • Java Beans .. Python Pickles .. 我想扼杀那些想出这些可爱东西的书呆子

标签: python pickle graceful-degradation


【解决方案1】:

下面的内容怎么样,它是一个包装器,您可以使用它来将一些模块(可能是任何模块)包装在可以腌制的东西中。然后,您可以对 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】:

    嗯,是这样的吗?

    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 类的扩展...

    【讨论】:

      【解决方案3】:

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

      本质上,您需要提供一个函数,在给定类型实例的情况下,将其简化为元组——使用与 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,因此包含 pickle 的字符串 s 很容易检查:它指示 unpickling 调用内置的导入函数,字符串为 sys作为其唯一的论据。 z 表明,这确实将内置的 sys 模块作为 unpickling 的结果返回给我们。

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

      【讨论】:

      • @Alex Martelli:当我使用 copy_reg.pickle 时,此更改的相关范围是什么?我希望人们能够在不更改任何可能破坏他们程序的系统值的情况下导入我的作品。
      • copy_reg 是全球性的。但是,如果他们当前没有腌制模块(系统默认情况下这是不可能的),它就不能“破坏他们的程序”以使模块可以腌制。
      • @Alex Martelli:但是如果他们遇到同样的问题并以不同的方式定义模块酸洗,我们就会遇到问题。我相信礼貌而不是改变系统状态。我相信,当您在 Python 中导入某些模块时,您不必担心它会干扰系统的全局变量,并且拥有可以让您在模块中避免这种“不礼貌”的工具非常重要。
      • @cool-RR,我明白你的意思,但不会比验证模块不能被腌制更远(在启动时,尝试/除了尝试腌制模块)和允许您代码的潜在第三方重用者打开和关闭您的架构。允许重用不应该以干扰干净和可维护的使用为代价,因为允许模块酸洗的记录和设计方法不太可能导致您担心的不良影响(而且我从未见过任何框架泡菜模块...我见过很多!-)。
      猜你喜欢
      • 2011-08-29
      • 1970-01-01
      • 2014-09-30
      • 2013-01-18
      • 2011-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-22
      相关资源
      最近更新 更多