Карта многопроцессорного пула 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()
Другие вопросы по тегам