Python — Mutliprocess, функции-члены классов

Я не могу понять, из-за меня ли это или из-за многопроцессорного модуля, который есть в Python2.7. Кто-нибудь может понять, почему это не работает?

from multiprocessing import pool as mp
class encapsulation:
   def __init__(self):
       self.member_dict = {}
   def update_dict(self,index,value):
       self.member_dict[index] = value
encaps = encapsulation()
def method(argument):
   encaps.update_dict(argument,argument)
   print encaps.member_dict
p = mp() #sets up multiprocess pool of processors
p.map(method,sys.argv[1:]) #method is the function, sys.argv is the list of arguments to multiprocess
print encaps.member_dict
>>>{argument:argument}
>>>{}

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


person jwillis0720    schedule 04.07.2012    source источник


Ответы (1)


Несмотря на то, что вы инкапсулируете объект, многопроцессорный модуль в конечном итоге будет использовать локальную копию объекта в каждом процессе и никогда фактически не передаст вам ваши изменения. В этом случае вы неправильно используете Pool.map, так как он ожидает, что каждый вызов метода вернет результат, который затем будет отправлен обратно к вашему возвращаемому значению. Если вы хотите повлиять на общий объект, вам нужен менеджер, который будет координировать общую память:

Инкапсуляция общего объекта

from multiprocessing import Pool 
from multiprocessing import Manager
import sys

class encapsulation:
   def __init__(self):
       self.member_dict = {}
   def update_dict(self,index,value):
       self.member_dict[index] = value

encaps = encapsulation()

def method(argument):
   encaps.update_dict(argument,argument)
   # print encaps.member_dict       

manager = Manager()
encaps.member_dict = manager.dict()

p = Pool()
p.map(method,sys.argv[1:])

print encaps.member_dict

вывод

$ python mp.py a b c
{'a': 'a', 'c': 'c', 'b': 'b'}

Я бы предложил на самом деле не устанавливать общий объект в качестве атрибута члена, а скорее передавать его как аргумент или инкапсулировать сам общий объект, а затем передавать его значения в ваш dict. Общий объект не может храниться постоянно. Его необходимо опорожнить и выбросить:

# copy the values to a reg dict
encaps.member_dict = encaps.member_dict.copy()

Но это может быть даже лучше:

class encapsulation:
   def __init__(self):
       self.member_dict = {}
   # normal dict update
   def update_dict(self,d):
       self.member_dict.update(d)

encaps = encapsulation()

manager = Manager()
results_dict = manager.dict()

# pass in the shared object only
def method(argument):
   results_dict[argument] = argument    

p = Pool()
p.map(method,sys.argv[1:])

encaps.update_dict(results_dict)

Использование pool.map по назначению

Если бы вы использовали карту для возврата значений, это могло бы выглядеть так:

def method(argument):
   encaps.update_dict(argument,argument)
   return encaps.member_dict

p = Pool()
results = p.map(method,sys.argv[1:]) 
print results
# [{'a': 'a'}, {'b': 'b'}, {'c': 'c'}]

Вам нужно будет снова объединить результаты в свой dict:

for result in results:
    encaps.member_dict.update(result)
print encaps.member_dict
# {'a': 'a', 'c': 'c', 'b': 'b'}
person jdi    schedule 04.07.2012
comment
это сработало. Проблема сейчас в том, что когда я пытаюсь перебрать новый словарь, я получаю это. - person jwillis0720; 04.07.2012
comment
См. мое обновление о копировании данных из вашего общего объекта вместо того, чтобы хранить их. - person jdi; 04.07.2012
comment
Большое спасибо, какой отличный, хорошо продуманный ответ! - person jwillis0720; 08.07.2012
comment
Зачем вам нужно копировать данные из общего объекта? - person Sveltely; 08.07.2015
comment
@Sveltely, прошло много времени с тех пор, как я написал это, но, если мне не изменяет память, я полагаю, что это произошло потому, что dict, возвращенное из Manager, является Proxy. И его время жизни привязано к Manager, связанному с порожденным дочерним процессом. Поэтому, как только вы закончите генерировать общие данные, в идеале вы не захотите продолжать использовать прокси-сервер и собирать результаты в стандартный контейнер. - person jdi; 09.07.2015