简体   繁体   English

带有莳萝的模块中的 Pickle 类定义

[英]Pickle class definition in module with dill

My module contains a class which should be pickleable, both instance and definition I have the following structure:我的模块包含一个应该是pickleable的类,实例和定义我都有以下结构:

MyModule
|-Submodule
  |-MyClass

In other questions on SO I have already found that dill is able to pickle class definitions and surely enough it works by copying the definition of MyClass into a separate script and pickling it there, like this:在关于 SO 的其他问题中,我已经发现 dill 能够腌制类定义,当然它可以通过将MyClass的定义复制到一个单独的脚本中并在那里腌制它来工作,就像这样:

import dill as pickle

class MyClass(object):
    ...

instance = MyClass(...)
with open(..., 'wb') as file:
   pickle.dump(instance, file)

However, it does not work when importing the class:但是,在导入类时它不起作用:

Pickling:酸洗:

from MyModule.Submodule import MyClass
import dill as pickle

instance = MyClass(...)
with open(.., 'wb') as file:
    pickle.dump(instance, file)

Loading:加载:

import dill as pickle

with open(..., 'rb') as file:
    instance = pickle.load(file)

>>> ModuleNotFoundError: No module named 'MyModule'

I think the class definition is saved by reference, although it should not have as per default settings in dill.我认为类定义是通过引用保存的,尽管它不应该具有 dill 中的默认设置。 This is done correctly when MyClass is known as __main__.MyClass , which happens when the class is defined in the main script.MyClass被称为__main__.MyClass ,这是正确完成的,当在主脚本中定义类时会发生这种情况。

I am wondering, is there any way to detach MyClass from MyModule ?我想知道,有没有办法将MyClassMyModule分离出来? Any way to make it act like a top level import ( __main__.MyClass ) so dill knows how to load it on my other machine?有什么方法可以让它像顶级导入( __main__.MyClass )一样,所以 dill 知道如何将它加载到我的另一台机器上?

Relevant question: Why dill dumps external classes by reference no matter what相关问题: 为什么 dill 无论如何都要通过引用转储外部类

I'm the dill author.我是dill作者。 This is a duplicate of the question you refer to above.这是您在上面提到的问题的副本。 The relevant GitHub feature request is: https://github.com/uqfoundation/dill/issues/128 .相关的 GitHub 功能请求是: https : //github.com/uqfoundation/dill/issues/128

I think the larger issue is that you want to pickle an object defined in another file that is not installed.我认为更大的问题是您想要腌制另一个未安装的文件中定义的对象。 This is currently not possible, I believe.我相信这是目前不可能的。

As a workaround, I believe you may be able to pickle with dill.source by extracting the source code of the class (or module) and pickling that dynamically, or extracting the source code and compiling a new object in __main__ .作为一种解决方法,我相信您可以通过提取类(或模块)的源代码并动态进行酸洗,或者提取源代码并在__main__编译新对象来使用dill.source进行酸洗。

I managed to save the instance and definition of my class using the following dirty hack:我设法使用以下脏技巧保存了我的类的实例和定义:

class MyClass(object):
    def save(path):
        import __main__

        with open(__file__) as f:
            code = compile(f.read(), "somefile.py", 'exec')
            globals = __main__.__dict__
            locals = {'instance': self, 'savepath': path}
            exec(code, globals, locals)

if __name__ == '__main__':
    # Script is loaded in top level, MyClass is now available under the qualname '__main__.MyClass'
    import dill as pickle

    # copy the attributes of the 'MyModule.Submodule.MyClass' instance to a bew 'MyClass' instance.
    new_instance = MyClass.__new__(MyClass)
    new_instance.__dict__ = locals()['instance'].__dict__

    with open(locals()['savepath'], 'wb') as f:       
        pickle.dump(new_instance, f)

Using the exec statement the file can be executed from within __main__ , so the class definition will be saved as well.使用exec语句可以从__main__执行文件,因此类定义也将被保存。 This script should not be executed as main script without using the save function.在不使用保存功能的情况下,不应将此脚本作为主脚本执行。

Dill indeed only stores definitions of objects in __main__ , and not those in modules, so one way around this problem is to redefine those objects in main: Dill 确实只在__main__存储对象的定义,而不是在模块中存储对象的定义,所以解决这个问题的一种方法是在 main 中重新定义这些对象:

def mainify(obj):
    import __main__
    import inspect
    import ast

    s = inspect.getsource(obj)
    m = ast.parse(s)
    co = compile(m, '<string>', 'exec')
    exec(co, __main__.__dict__)

And then:进而:

from MyModule.Submodule import MyClass
import dill as pickle

mainify(MyClass)
instance = MyClass(...)
with open(.., 'wb') as file:
    pickle.dump(instance, file)

Now you should be able to load the pickle from anywhere, even where the MyModule.Submodule is not available.现在您应该能够从任何地方加载泡菜,即使MyModule.Submodule不可用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM