Многопроцессорный пул Python возвращает тот же вывод в цикле - PullRequest
0 голосов
/ 02 июля 2018

Я использую многопроцессорную работу. Пул в Python 3.6, через Spyder 3.6.5 на компьютере с Windows 10. Цель состоит в том, чтобы получить выходные данные из простой функции возведения в квадрат путем импорта нескольких входных данных (в этом примере для практических задач включены только 4 значения). Приведенный ниже код работает нормально:

import numpy as np
import multiprocessing

from multiprocessing import Pool

data=[]
data.append(np.array([1,2]))
data.append(np.array([4,5]))

Output=np.zeros((2,2))


for i in range (0,2):

    data1=data[i]

    def square(x):
        return x*x

    if __name__ == '__main__':
        __spec__ = "ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>)"
        with Pool(multiprocessing.cpu_count()) as p:
            output = p.map(square, data1, chunksize=10)
            p.close()
            output=np.asarray(output)
            Output[i]=output

в то время, когда я хочу указать входные квадратные значения функции (x) как:

def square(ii):
    x=data1[ii]
    return x*x

цикл for выполняется два раза (из-за 'for i в диапазоне (0,2)'), но результаты p.map одинаковы при каждом запуске и равны второму запуску, т. Е. Вместо Output = np.array ([[1,4], [16,25]]) Я получаю Output = np.array ([[16,25], [16,25]]). Кажется, что цикл for выполняется два раза с i = 1, а не как в первом цикле i = 0, а во втором i = 1.

Есть идеи, что я делаю не так?

1 Ответ

0 голосов
/ 02 июля 2018

Замыкания в python не копируют значения переменных, которые они закрывают. Они просто сохраняют ссылку на эту область. Во второй попытке функция square обращается к data1, как если бы это было другое значение в обеих итерациях, но на самом деле это ссылка на одну и ту же переменную. Ко времени, когда многопроцессорный модуль запускает новый процесс и вызывает square, переменная уже изменилась.

Попробуйте это, например:

res = []
for i in range(5):
    def square():
        return i * i
    res.append(square)


print([f() for f in res])

Что касается решения, вы можете вручную создать новую область с соответствующим значением в каждой итерации.

Вызов функции создает новую область видимости, так что вы можете определить вспомогательную функцию, подобную этой, вне цикла

def square_creater(data1):
    def square(ii):
        x = data1[ii]
        return x * x
    return square

и затем на каждой итерации используйте

square = square_creater(data1)

Это должно сработать.

...