Карта многопроцессорного пула Python: AttributeError: Невозможно выбрать локальный объект
У меня есть метод внутри класса, который должен выполнять большую работу в цикле, и я хотел бы распределить работу по всем моим ядрам.
Я написал следующий код, который работает, если я использую обычный map
, но с pool.map
возвращает ошибку.
import multiprocessing
pool = multiprocessing.Pool(multiprocessing.cpu_count() - 1)
class OtherClass:
def run(sentence, graph):
return False
class SomeClass:
def __init__(self):
self.sentences = [["Some string"]]
self.graphs = ["string"]
def some_method(self):
other = OtherClass()
def single(params):
sentences, graph = params
return [other.run(sentence, graph) for sentence in sentences]
return list(pool.map(single, zip(self.sentences, self.graphs)))
SomeClass().some_method()
Ошибка:
AttributeError: Невозможно выбрать локальный объект SomeClass.some_method..single.
Почему это не мариновать single
? Я даже пытался двигатьсяsingle
к глобальной области видимости модуля (не внутри класса - делает его независимым от контекста):
import multiprocessing
pool = multiprocessing.Pool(multiprocessing.cpu_count() - 1)
class OtherClass:
def run(sentence, graph):
return False
def single(params):
other = OtherClass()
sentences, graph = params
return [other.run(sentence, graph) for sentence in sentences]
class SomeClass:
def __init__(self):
self.sentences = [["Some string"]]
self.graphs = ["string"]
def some_method(self):
return list(pool.map(single, zip(self.sentences, self.graphs)))
SomeClass().some_method()
и я получаю следующую ошибку:
AttributeError: Невозможно получить атрибут 'single' для модуля 'main' из '.../test.py'
1 ответ
Вы запускаете пул до того, как определите свою функцию и классы, таким образом, дочерние процессы не могут наследовать какой-либо код. Передвиньте бассейн вверх и защитите его if __name__ == '__main__':
import multiprocessing
class OtherClass:
def run(self, sentence, graph):
return False
def single(params):
other = OtherClass()
sentences, graph = params
return [other.run(sentence, graph) for sentence in sentences]
class SomeClass:
def __init__(self):
self.sentences = [["Some string"]]
self.graphs = ["string"]
def some_method(self):
return list(pool.map(single, zip(self.sentences, self.graphs)))
if __name__ == '__main__': # <- prevent RuntimeError for 'spawn'
# and 'forkserver' start_methods
with multiprocessing.Pool(multiprocessing.cpu_count() - 1) as pool:
print(SomeClass().some_method())
Я случайно обнаружил очень неприятное решение. Это работает, пока вы используетеdef
заявление. Если вы объявляете функцию, которую хотите использовать вPool.map
с global
ключевое слово в начале функции, которая его решает. Но я бы не стал на это полагаться в серьезных приложениях.
import multiprocessing
pool = multiprocessing.Pool(multiprocessing.cpu_count() - 1)
class OtherClass:
def run(sentence, graph):
return False
class SomeClass:
def __init__(self):
self.sentences = [["Some string"]]
self.graphs = ["string"]
def some_method(self):
global single # This is ugly, but does the trick XD
other = OtherClass()
def single(params):
sentences, graph = params
return [other.run(sentence, graph) for sentence in sentences]
return list(pool.map(single, zip(self.sentences, self.graphs)))
SomeClass().some_method()