【问题标题】:How to pickle functions/classes defined in __main__ (python)如何腌制__main__(python)中定义的函数/类
【发布时间】:2012-08-08 14:39:10
【问题描述】:

我希望能够从 __main__ 中腌制一个函数或类,但有一个明显的问题(在其他帖子中提到),腌制的函数/类位于 __main__ 命名空间中,而在另一个脚本/模块中取消腌制将失败。

我有以下可行的解决方案,是否有理由不这样做?

myscript.py 中有以下内容:

import myscript
import pickle

if __name__ == "__main__":               

    print pickle.dumps(myscript.myclass())

else:

    class myclass:
        pass

edit:取消腌制将在有权访问 myscript.py 并且可以执行import myscript 的脚本/模块中完成。目的是使用像parallel python 这样的解决方案来远程调用函数,并能够编写一个简短的独立脚本,其中包含可以远程访问的函数/类。

【问题讨论】:

  • 这种结构不是违背了结合脚本和模块的目的吗?您可以制作单独的文件。

标签: python pickle


【解决方案1】:

Pickle 似乎在查看类和函数定义的 ma​​in 范围。从你要解压的模块内部,试试这个:

import myscript
import __main__
__main__.myclass = myscript.myclass
#unpickle anywhere after this

【讨论】:

    【解决方案2】:

    您可以通过导入__main__ 并使用该模块中可用的方法来更好地处理全局对象。这就是dill 所做的,以便在 python 中序列化几乎任何东西。基本上,当 dill 序列化一个交互式定义的函数时,它会在序列化和反序列化方面对__main__ 使用一些名称修饰,从而使__main__ 成为有效模块。

    >>> import dill
    >>> 
    >>> def bar(x):
    ...   return foo(x) + x
    ... 
    >>> def foo(x):
    ...   return x**2
    ... 
    >>> bar(3)
    12
    >>> 
    >>> _bar = dill.loads(dill.dumps(bar))
    >>> _bar(3)
    12
    

    实际上,dill 将它的类型注册到 pickle 注册表中,所以如果你有一些使用 pickle 的黑盒代码并且你不能真正编辑它,那么只需导入 dill 就可以神奇地使它工作而无需猴子补丁第三方代码。

    或者,如果您希望将整个解释器会话作为“python 图像”发送过来,dill 也可以这样做。

    >>> # continuing from above
    >>> dill.dump_session('foobar.pkl')
    >>>
    >>> ^D
    dude@sakurai>$ python
    Python 2.7.5 (default, Sep 30 2013, 20:15:49) 
    [GCC 4.2.1 (Apple Inc. build 5566)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import dill
    >>> dill.load_session('foobar.pkl')
    >>> _bar(3)
    12
    

    您可以轻松地通过 ssh 将图像发送到另一台计算机,然后从您离开的地方开始,只要存在 pickle 的版本兼容性以及有关 python 更改和正在安装的东西的常见警告。

    我实际上使用 dill 序列化对象并使用 parallel python、多处理和 mpi4py 在并行资源之间发送它们。我将这些方便地汇总到 pathos 包中(以及用于 MPI 的 pyina),它为不同的并行批处理后端提供了统一的 map 接口。

    >>> # continued from above
    >>> from pathos.multiprocessing import ProcessingPool as Pool
    >>> Pool(4).map(foo, range(10))
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    >>>
    >>> from pyina.launchers import MpiPool
    >>> MpiPool(4).map(foo, range(10))
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    

    还有非阻塞和迭代映射以及非平行管道连接。我还有一个用于pp 的 pathos 模块,但是,它对于__main__ 中定义的函数有些不稳定。我正在努力改进它。如果您愿意,请 fork the code on github 并帮助使 pp 更好地用于 __main__ 中定义的函数。 pp 不能很好地腌制的原因是 pp 通过使用临时文件对象和读取解释器会话的历史来实现序列化技巧......所以它不会以与多处理或 mpi4py 相同的方式序列化对象。我有一个 dill 模块 dill.source 可以无缝地执行 pp 使用的相同类型的酸洗,但它是相当新的。

    【讨论】:

    • 但是,如果您在计算机上加载 dill 对象或 dill 会话,而无法访问会话中导入的对象或在保存的对象中使用,它似乎不起作用。例如``` from foo import bar x = dict(bar=bar) with open('test.pkl', 'wb') as fp: dill.dump(x, fp) ``` 然后在计算机或会话 w/无法访问您的 PYTHONPATH,如果您这样做: ``` with open('test.pkl', 'rb') as fp: model = dill.load(fp) # ^^ 导致“没有名为 foo 的模块”错误 ` ``
    • @HillarySanders:如果您试图腌制一个引用第二个本地(即已卸载)模块的脚本......那么,是的 - 除非您也传输非已安装的模块。
    • 嗯,确实。 github.com/uqfoundation/dill/issues/123 相关问题 #123 是否可能在某个时候实施?
    • 看到一个库真的很酷,例如获取一个python对象(例如类实例)并递归地提取所有依赖项,保存它们,这样它们就可以加载到另一台机器上,而无法访问原始源代码。这甚至可以与自动生成 requirements.txt 文件相结合。我觉得以前一定有人这样做过,但我在网上找不到实现:)。
    • @HillarySanders:这是正确的问题。这并不容易,而且以前没有做过。 dill 做了很多你正在寻找的事情(获取依赖项等),但特别是不获取本地导入的模块。在大多数情况下,它确实获得了大多数依赖项,只是没有本地模块。
    【解决方案3】:

    如果你试图腌制一些东西以便你可以在其他地方使用它,而不是test_script,那是行不通的,因为pickle(显然)只是试图从模块中加载函数。这是一个例子:

    test_script.py

    def my_awesome_function(x, y, z):
        return x + y + z
    

    picklescript.py

    import pickle
    import test_script
    with open("awesome.pickle", "wb") as f:
        pickle.dump(test_script.my_awesome_function, f)
    

    如果你运行python picklescript.py,然后更改test_script的文件名,当你尝试加载函数时,它会失败。例如

    运行这个:

    import pickle
    with open("awesome.pickle", "rb") as f:
        pickle.load(f)
    

    会给你以下回溯:

    Traceback (most recent call last):
      File "load_pickle.py", line 3, in <module>
        pickle.load(f)
      File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/pickle.py", line 1378, in load
        return Unpickler(file).load()
      File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/pickle.py", line 858, in load
        dispatch[key](self)
      File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/pickle.py", line 1090, in load_global
        klass = self.find_class(module, name)
      File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/pickle.py", line 1124, in find_class
        __import__(module)
    ImportError: No module named test_script
    

    【讨论】:

    • 我同意。但是,我正在寻找模块/脚本可以访问 test_script.py 的解决方案,请参阅我最近的编辑。
    猜你喜欢
    • 2019-04-04
    • 2014-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多