Невозможно раскрыть выражение Pyparsing с помощью метода setParseAction(). Требуется для многопроцессорной обработки
Моя первоначальная проблема в том, что я пытаюсь сделать следующее:
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()
декодер немного задействован для описания здесь, но важно то, что декодер является объектом, который инициализируется выражением PyParsing, которое вызывает setParseAction(). Это не позволяет определить, что использует многопроцессорность, и это, в свою очередь, не позволяет выполнить вышеуказанный код.
Теперь вот проблема рассола /PyParsing, которую я выделил и упростил.
Следующий код дает сообщение об ошибке из-за сбоя рассола.
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 not found as pyparsing.wrapper
Теперь, если вы удалите вызов .setParseAction(my_pa_func), он будет работать без проблем:
pickle.dumps(Word(nums))
Как я могу обойти это? Многопроцессор использует рассол, поэтому я не могу этого избежать. Патсовый пакет, который предположительно использует укроп, недостаточно зрелый, по крайней мере, у меня возникают проблемы с его установкой на моем Windows-64bit. Я действительно почесываю голову здесь.
Ответы
Ответ 1
ОК, вот решение, вдохновленное rockportrocker: Ошибка трассировки Python для мультипроцессора
Идея состоит в том, чтобы укротить объект, который не может быть маринован при передаче его назад и вперед между процессами, а затем "не допустить" его после того, как он был передан:
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())
Ответ 2
https://docs.python.org/2/library/pickle.html#what-can-be-pickled-and-unpickled
Многопроцессорный .Pool использует протокол Pickle для сериализации имен функций и модулей (в вашем примере setParseAction и pyparse), которые доставляются через Pipe к дочернему процессу.
Детский процесс, после его получения, импортирует модуль и пытается вызвать функцию. Проблема в том, что то, что вы проходите, не является функцией, а методом. Чтобы решить эту проблему, протокол Pickle должен быть достаточно умен, чтобы создать объект "Word" с параметром "user", а затем вызвать метод setParseAction. Поскольку обработка этих случаев слишком сложна, протокол Pickle предотвращает сериализацию функций верхнего уровня.
Чтобы решить вашу проблему, вы инструктируете модуль Pickle о том, как сериализовать метод setParseAction (https://docs.python.org/2/library/pickle.html#pickle-protocol), или вы реорганизуете свой код в способ, которым передается пул Pool.apply_async является сериализуемым.
Что делать, если вы передаете объект Word дочернему процессу, и вы позволите ему вызвать Word(). setParseAction()?
Ответ 3
Я бы предложил pathos.multiprocessing
, как вы упомянули. Конечно, я автор pathos
, поэтому я думаю, что это не сюрприз. Похоже, что может быть ошибка distutils
, с которой вы работаете, как указано здесь: https://github.com/uqfoundation/pathos/issues/49.
Ваше решение, использующее dill
, является хорошим обходным решением. Вы также можете отказаться от установки всего пакета pathos
и просто установить pathos
fork пакета multiprocessing
(который использует dill
вместо pickle
). Вы можете найти его здесь: http://dev.danse.us/packages или здесь: https://github.com/uqfoundation/pathos/tree/master/external,