Python 中的优雅降级 pickling

发布于 2024-08-04 02:20:31 字数 492 浏览 3 评论 0原文

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

我想要一个在 Python 中 pickle 对象的优雅降级方式。

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

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

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

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


(1) 一个问题是该模块可能会从其内部引用其他模块。

(You may read this question for some background)

I would like to have a gracefully-degrading way to pickle objects in Python.

When pickling an object, let's call it the main object, sometimes the Pickler raises an exception because it can't pickle a certain sub-object of the main object. For example, an error I've been getting a lot is "can’t pickle module objects." That is because I am referencing a module from the main object.

I know I can write up a little something to replace that module with a facade that would contain the module's attributes, but that would have its own issues(1).

So what I would like is a pickling function that automatically replaces modules (and any other hard-to-pickle objects) with facades that contain their attributes. That may not produce a perfect pickling, but in many cases it would be sufficient.

Is there anything like this? Does anyone have an idea how to approach this?


(1) One issue would be that the module may be referencing other modules from within it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

望她远 2024-08-11 02:20:31

您可以决定并实现如何对任何先前不可picklable的类型进行pickle和unpickled:请参阅标准库模块 copy_reg< /a>(在 Python 3.* 中重命名为 copyreg)。

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

通常,您返回的元组有 2 个项目:一个可调用项和一个要传递给它的参数元组。可调用对象必须注册为“安全构造函数”,或者等效地具有具有 true 值的属性 __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 模块作为 unpickle 的结果返回给了我们。

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

You can decide and implement how any previously-unpicklable type gets pickled and unpickled: see standard library module copy_reg (renamed to copyreg in Python 3.*).

Essentially, you need to provide a function which, given an instance of the type, reduces it to a tuple -- with the same protocol as the reduce special method (except that the reduce special method takes no arguments, since when provided it's called directly on the object, while the function you provide will take the object as the only argument).

Typically, the tuple you return has 2 items: a callable, and a tuple of arguments to pass to it. The callable must be registered as a "safe constructor" or equivalently have an attribute __safe_for_unpickling__ with a true value. Those items will be pickled, and at unpickling time the callable will be called with the given arguments and must return the unpicked object.

For example, suppose that you want to just pickle modules by name, so that unpickling them just means re-importing them (i.e. suppose for simplicity that you don't care about dynamically modified modules, nested packages, etc, just plain top-level modules). Then:

>>> 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)>

I'm using the old-fashioned ASCII form of pickle so that s, the string containing the pickle, is easy to examine: it instructs unpickling to call the built-in import function, with the string sys as its sole argument. And z shows that this does indeed give us back the built-in sys module as the result of the unpickling, as desired.

Now, you'll have to make things a bit more complex than just __import__ (you'll have to deal with saving and restoring dynamic changes, navigate a nested namespace, etc), and thus you'll have to also call copy_reg.constructor (passing as argument your own function that performs this work) before you copy_reg the module-saving function that returns your other function (and, if in a separate run, also before you unpickle those pickles you made using said function). But I hope this simple cases helps to show that there's really nothing much to it that's at all "intrinsically" complicated!-)

怼怹恏 2024-08-11 02:20:31

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

How about the following, which is a wrapper you can use to wrap some modules (maybe any module) in something that's pickle-able. You could then subclass the Pickler object to check if the target object is a module, and if so, wrap it. Does this accomplish what you desire?

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)
长伴 2024-08-11 02:20:31

嗯,像这样的吗?

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

Hmmm, something like this?

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

Obviously, this would go into an extension of the pickle class with a reimplemented dump method...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文