【问题标题】:Can't pickle Pyparsing expression with setParseAction() method. Needed for multiprocessing无法使用 setParseAction() 方法腌制 Pyparsing 表达式。需要多处理
【发布时间】:2015-03-09 03:10:35
【问题描述】:

我最初的问题是我正在尝试执行以下操作:

def submit_decoder_process(decoder, input_line):
    decoder.process_line(input_line)
    return decoder

self.pool = Pool(processes=num_of_processes)
self.pool.apply_async(submit_decoder_process, [decoder, input_line]).get()

decoder 在这里描述起来有点牵强,但重要的是,decoder 是一个使用调用 setParseAction() 的 PyParsing 表达式初始化的对象。这使多处理使用的泡菜失败,这反过来又使上述代码失败。

现在,这是我分离和简化的 pickle/PyParsing 问题。 由于 pickle 失败,以下代码会产生错误消息。

import pickle
from pyparsing import *

def my_pa_func():
    pass

pickle.dumps(Word(nums).setParseAction(my_pa_func))

错误信息:

pickle.PicklingError: Can't pickle <function wrapper at 0x00000000026534A8>: it's not found as pyparsing.wrapper

现在,如果您删除调用 .setParseAction(my_pa_func),它将毫无问题地工作:

pickle.dumps(Word(nums))

我怎样才能绕过它?多处理使用泡菜,所以我想我无法避免它。据称使用 dill 的 pathos 包还不够成熟,至少,我在 Windows-64 位上安装它时遇到问题。我真的在这里摸不着头脑。

【问题讨论】:

  • ~~rant~~: pathos 已经接近 10 年了......几乎一直在积极发展(嗯,定期)。它使用setuptools 轻松安装。如果您使用pre 标志,则可以使用pip 安装它。我看不出它为什么不是一个“成熟”的包......除了最后一个稳定版本的版本号(在版本号标准改变之前不是问题)。总之,叹气。待定的新版本将处理 pip 安装问题纯粹由于版本编号。

标签: python pickle python-multithreading pyparsing python-multiprocessing


【解决方案1】:

https://docs.python.org/2/library/pickle.html#what-can-be-pickled-and-unpickled

multiprocessing.Pool 使用 Pickle 的协议来序列化通过管道传递给子进程的函数和模块名称(在您的示例中为 setParseAction 和 pyparse)。

子进程,一旦接收到它们,它就会导入模块并尝试调用函数。问题是您传递的不是函数而是方法。为了解决这个问题,Pickle 协议应该足够聪明,用 'user' 参数构建 'Word' 对象,然后调用 setParseAction 方法。由于处理这些情况过于复杂,Pickle 协议会阻止您序列化非顶级函数。

要解决您的问题,您可以指示 Pickle 的模块如何序列化 setParseAction 方法 (https://docs.python.org/2/library/pickle.html#pickle-protocol),或者重构您的代码,使传递给 Pool.apply_async 的内容可序列化。

如果将 Word 对象传递给子进程并让它调用 Word().setParseAction() 会怎样?

【讨论】:

    【解决方案2】:

    好的,这是由rocksportrocker启发的解决方案:Python multiprocessing pickling error

    这个想法是在进程之间来回传递时对无法腌制的对象进行挖取,然后在传递之后“取消挖取”它:

    from multiprocessing import Pool
    import dill
    
    def submit_decoder_process(decoder_dill, input_line):
        decoder = dill.loads(decoder_dill)  # undill after it was passed to a pool process
        decoder.process_line(input_line)
        return dill.dumps(decoder)  # dill before passing back to parent process
    
    self.pool = Pool(processes=num_of_processes)
    
    # Dill before sending to a pool process
    decoder_processed = dill.loads(self.pool.apply_async(submit_decoder_process, [dill.dumps(decoder), input_line]).get())
    

    【讨论】:

    • 很高兴您能够在 dill 模块的帮助下解决这个问题。我过去对 pyparsing 进行了更改以支持酸洗,但我从未尝试使用解析操作来酸洗解析器 - 很好的解决方案,我会看看是否有办法将其合并到 pyparsing 中。
    【解决方案3】:

    我建议pathos.multiprocessing,正如你提到的。当然,我是pathos 作者,所以我想这并不奇怪。看来您可能遇到了distutils 错误,如下所示:https://github.com/uqfoundation/pathos/issues/49

    您使用dill 的解决方案是一个很好的解决方法。您也可以放弃安装整个pathos 包,而只需安装multiprocessing 包的pathos fork(它使用dill 而不是pickle)。你可以在这里找到它:http://dev.danse.us/packages 或在这里:https://github.com/uqfoundation/pathos/tree/master/external

    【讨论】:

    • 嗨,迈克,是我在 github 上打开了这个问题。实际上,首先我尝试使用 pathos,但由于各种原因它没有为我安装,我不得不想出某种解决方法。无论如何,我期待在所有问题都得到解决时使用 pathos。
    • 是的,我发现是你。我为遇到与您遇到的相同 python 错误的其他人添加了上述内容,因此他们也会有您的票证的链接。无论如何,没有好的活动代码可以解决所有问题。 :) 但我正在积极努力让 pathos 更易于安装。
    猜你喜欢
    • 1970-01-01
    • 2010-12-21
    • 1970-01-01
    • 2014-09-29
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多