【问题标题】:How to get around the pickling error of python multiprocessing without being in the top-level?如何在不处于顶层的情况下绕过 python 多处理的酸洗错误?
【发布时间】:2015-05-03 12:29:09
【问题描述】:

我已经多次研究过这个问题,但没有找到适合我或我理解的解决方法,所以请多多包涵。

基本上,我有一个分层的功能组织,这阻止了我在顶层进行多处理。不幸的是,我不相信我可以改变程序的布局——因为我需要在初始输入之后创建的所有变量。

例如,假设我有这个:

import multiprocessing

  def calculate(x):
    # here is where I would take this input x (and maybe a couple more inputs)
    # and build a larger library of variables that I use further down the line

    def domath(y):
      return x * y

    pool = multiprocessing.Pool(3)
    final= pool.map(domath, range(3))

calculate(2)

这会产生以下错误:

Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

我在考虑全局变量,但我担心我必须定义太多,这可能会大大降低我的程序速度。 有没有无需重新构建整个程序的解决方法?

【问题讨论】:

  • 您可能想阅读我在尝试解决pretty much the same error时选择的答案。
  • 嘿肖恩,在看完你的问题后,恐怕我的解决方案可能超出了我的理解范围。无论如何,您可以给我一个更概念性的关于这些函数在打包和解包时的作用吗?
  • 当然。让我一起获取一些示例代码。

标签: python multiprocessing pickle python-multiprocessing


【解决方案1】:

你遇到的问题其实是一个特性。 pickle 源实际上是为了防止这种行为而设计的,以防止恶意代码被执行。请在解决任何适用的安全实施时考虑到这一点。

首先我们有一些导入。

import marshal
import pickle
import types

这里我们有一个函数,它接受一个函数作为参数,腌制对象的各个部分,然后返回一个包含所有部分的元组:

def pack(fn):
    code = marshal.dumps(fn.__code__)
    name = pickle.dumps(fn.__name__)
    defs = pickle.dumps(fn.__defaults__)
    clos = pickle.dumps(fn.__closure__)
    return (code, name, defs, clos)

接下来我们有一个函数,它接受我们转换函数的四个部分。它翻译这四个部分,然后从这些部分中创建然后返回一个函数。您应该注意,这里重新引入了全局变量,因为我们的流程不处理这些:

def unpack(code, name, defs, clos):
    code = marshal.loads(code)
    glob = globals()
    name = pickle.loads(name)
    defs = pickle.loads(defs)
    clos = pickle.loads(clos)
    return types.FunctionType(code, glob, name, defs, clos)

这里我们有一个测试函数。请注意,我将导入放在函数的范围内。全局变量不通过我们的酸洗过程处理:

def test_function(a, b):
    from random import randint
    return randint(a, b)

最后我们打包测试对象并打印结果以确保一切正常:

packed = pack(test_function)
print((packed))

最后,我们解压我们的函数,将它分配给一个变量,调用它,然后打印它的输出:

unpacked = unpack(*packed)
print((unpacked(2, 20)))

如果您有任何问题,请发表评论。

【讨论】:

  • 感谢您花时间详细解释!我正在尝试将此应用于我上面发布的示例,但我仍然遇到问题。当它解包时,它不会返回与以前相同的格式化函数吗?当我运行此方法时,我仍然遇到酸洗错误(这可能意味着我使用不正确)。编辑:您如何看待@mike 的回答?
  • @Tim 是的,“解包”功能应该和原来的一样。如果要存储原始源,则需要处理字符串。至于迈克的回答,我认为这可能是一个更好的选择。
  • @SeanPedersen:这基本上就是 dill 为您所做的,除了它创建与存储的相同的可调用类型......所以如果你腌制一个 lambda,你会得到一个 lambda,或者如果你腌制一个绑定的方法,你得到一个绑定的方法,等等。在大多数情况下,它还会为您处理全局变量,并且还使您能够将 __main__ 视为一个模块。
【解决方案2】:

您可以使用pathos.multiprocessing,它是multiprocessing 的一个分支,它使用dill 序列化程序而不是pickledill 可以在 python 中序列化几乎任何东西。然后,无需编辑您的代码。

>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> 
>>> def calculate(x):
...   def domath(y):
...     return x*y
...   return Pool().map(domath, range(3))
... 
>>> calculate(2)
[0, 2, 4]

你甚至可以对它发疯……因为大多数东西都是腌制的。无需使用纯multiprocessing 编写的奇怪的非pythonic 解决方案。

>>> class Foo(object):
...   def __init__(self, x):
...     self.x = x
...   def doit(self, y):
...     return ProcessingPool().map(self.squared, calculate(y+self.x))
...   def squared(self, z):
...     return z*z
... 
>>> def thing(obj, y):
...   return getattr(obj, 'doit')(y)
... 
>>> ProcessingPool().map(thing, ProcessingPool().map(Foo, range(3)), range(3))
[[0, 0, 0], [0, 4, 16], [0, 16, 64]]

在此处获取pathoshttps://github.com/uqfoundation

【讨论】:

  • 谢谢迈克!效果很好。关于trac.mystic.cacr.caltech.edu/project/pathos/wiki/Installation 上可用文件的快速说明此版本(至少在我下载它时)不包含多处理模块。我认为这可能是一些用户在通过 pathos 导入多处理时遇到问题的原因。从 github 而不是 pathos 页面下载文件后,我拥有了所需的一切。
  • 酷。是的,我知道..谢谢你的提醒。您提到的pathos wiki 链接上的版本非常旧(时间戳:06/28/10 17:50)。 github 代码是最新的,新版本“迫在眉睫”。届时我将使用新的稳定版本更新所有链接。
  • 另请注意,JobLib 具有特殊的序列化模式,可以绕过内置 pickle 的大多数标准问题。
  • @deepelement:大多数python包,如joblib,进行高级序列化依赖于dillcloudpickle
  • @MikeMcKerns 你完全正确。看起来joblib 目前在cloudpickle 上,正在研发dill
【解决方案3】:

把嵌入的功能拿出来怎么样?

这在我看来是最清晰的解决方案(因为你没有给出预期的输出,我不得不猜测):

$ cat /tmp/tmp.py
import multiprocessing

def calculate(x):
    # here is where I would take this input x (and maybe a couple more inputs)
    # and build a larger library of variables that I use further down the line

    pool = multiprocessing.Pool(3)
    _lst = [(x, y) for x in (x,) for y in range(3)]
    final= pool.map(domath, _lst)
    print(final)

def domath(l):
    return l[0] * l[1]

calculate(2)

$ python /tmp/tmp.py
[0, 2, 4]

$

【讨论】:

    猜你喜欢
    • 2021-04-29
    • 2018-07-26
    • 2015-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多