Python decorator с многопроцессорной обработкой терпит неудачу



Я хотел бы использовать декоратор для функции, которую я впоследствии передам в многопроцессорный пул. Однако код завершается с ошибкой "PicklingError: Can't pickle: attribute lookup __builtin__.сбой функции". Я не совсем понимаю, почему это не удается здесь. Я уверен, что это что-то простое, но я не могу найти его. Ниже приведен минимальный "рабочий" пример. Я думал, что использование функции functools будет достаточно, чтобы это сработало.



Если я закомментирую украшение функции, оно работает без вопрос. Что это за multiprocessing, что я здесь не понимаю? Есть ли какой-нибудь способ заставить это работать?



Edit : после добавления как вызываемого класса decorator , так и функции decorator, оказывается, что функция decorator работает так, как ожидалось. Вызываемый декоратор класса продолжает давать сбои. Что такого есть в вызываемой версии класса, что удерживает ее от маринования?



import random
import multiprocessing
import functools

class my_decorator_class(object):
def __init__(self, target):
self.target = target
try:
functools.update_wrapper(self, target)
except:
pass

def __call__(self, elements):
f = []
for element in elements:
f.append(self.target([element])[0])
return f

def my_decorator_function(target):
@functools.wraps(target)
def inner(elements):
f = []
for element in elements:
f.append(target([element])[0])
return f
return inner

@my_decorator_function
def my_func(elements):
f = []
for element in elements:
f.append(sum(element))
return f

if __name__ == '__main__':
elements = [[random.randint(0, 9) for _ in range(5)] for _ in range(10)]
pool = multiprocessing.Pool(processes=4)
results = [pool.apply_async(my_func, ([e],)) for e in elements]
pool.close()
f = [r.get()[0] for r in results]
print(f)
519   1  

1 ответ:

Проблема в том, что рассол должен иметь какой-то способ собрать все, что вы рассолите. Смотрите здесь список того, что можно мариновать:

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

При мариновании my_func необходимо мариновать следующие компоненты:

  • Экземпляр класса my_decorator_class, называемый my_func

    Это прекрасно. Pickle будет хранить имя класса и мариновать его содержимое __dict__. Когда распаковывая, он использует имя, чтобы найти класс, затем создает экземпляр и заполняет содержимое __dict__. Однако содержание __dict__ представляет собой проблему...

  • Экземпляр исходного файла my_func, который хранится в файле my_func.цель

    Это не очень хорошо. Это функция на высшем уровне, и обычно их можно замариновать. Рассол будет хранить имя функции. Проблема, однако, в том, что имя "my_func" больше не привязано к неотмеченной функции, это привязка к оформленной функции. Это означает, что Пикл не сможет найти функцию undecorated, чтобы воссоздать объект. К сожалению, pickle не имеет никакого способа узнать, что объект, который он пытается замариновать, всегда можно найти под именем main. my_func.

Вы можете изменить его следующим образом, и он будет работать:

import random
import multiprocessing
import functools

class my_decorator(object):
    def __init__(self, target):
        self.target = target
        try:
            functools.update_wrapper(self, target)
        except:
            pass

    def __call__(self, candidates, args):
        f = []
        for candidate in candidates:
            f.append(self.target([candidate], args)[0])
        return f

def old_my_func(candidates, args):
    f = []
    for c in candidates:
        f.append(sum(c))
    return f

my_func = my_decorator(old_my_func)

if __name__ == '__main__':
    candidates = [[random.randint(0, 9) for _ in range(5)] for _ in range(10)]
    pool = multiprocessing.Pool(processes=4)
    results = [pool.apply_async(my_func, ([c], {})) for c in candidates]
    pool.close()
    f = [r.get()[0] for r in results]
    print(f)

Вы заметили, что функция декоратора работает, когда класс этого не делает. Я считаю, что это потому, что functools.wraps изменяет украшенный функция так, чтобы она имела имя и другие свойства функции, которую она обертывает. Насколько модуль маринования может сказать, он неотличим от обычной функции верхнего уровня, поэтому он маринует его, сохраняя его имя. После распаковки имя привязывается к оформленной функции, поэтому все работает.

Comments

    Ничего не найдено.