python - 使用 concurrent.futures.ProcessPoolExecutor 动态创建函数的限制

标签 python multiprocessing pickle

我正在尝试使用我在其他函数中动态创建的函数进行多处理。如果提供给 ProcessPoolExecutor 的函数是模块级的,我似乎可以运行这些:

def make_func(a):
    def dynamic_func(i):
        return i, i**2 + a
    return dynamic_func

f_dyns = [make_func(a) for a in range(10)]
def loopfunc(i):
    return f_dyns[i](i)

with concurrent.futures.ProcessPoolExecutor(3) as executor:
    for i,r in executor.map(loopfunc, range(10)):
        print(i,":",r)

输出:

0 : 0
1 : 2
2 : 6
3 : 12
4 : 20
5 : 30
6 : 42
7 : 56
8 : 72
9 : 90

但是,如果多处理是由类函数启动的,我就不能这样做:

class Test:
    def __init__(self,myfunc):
        self.f = myfunc

    def loopfunc(self,i):
        return self.f(i)

    def run(self):
        with concurrent.futures.ProcessPoolExecutor(3) as executor:
            for i,r in executor.map(self.loopfunc, range(10)):
                print(i,":",r)

o2 = Test(make_func(1))
o2.run()

输出:

Traceback (most recent call last):
  File "/home/farmer/anaconda3/envs/general/lib/python3.6/multiprocessing/queues.py", line 234, in _feed
    obj = _ForkingPickler.dumps(obj)
  File "/home/farmer/anaconda3/envs/general/lib/python3.6/multiprocessing/reduction.py", line 51, in dumps
    cls(buf, protocol).dump(obj)
AttributeError: Can't pickle local object 'make_func.<locals>.dynamic_func'

另一方面,如果我不在其中使用动态生成的函数,我可以在类函数上运行多处理。有什么办法解决这个问题吗?我尝试将动态生成的函数添加到“全局”字典中,但这似乎没有帮助:

def make_func_glob(a):
    def dynamic_func(i):
        return i, i**2 + a
    globals()['my_func_{0}'.format(a)] = dynamic_func

make_func_glob(1)
print("test:", my_func_1(3))
o3 = Test(my_func_1)
o3.run()

输出:

test: (3, 10)
Traceback (most recent call last):
  File "/home/farmer/anaconda3/envs/general/lib/python3.6/multiprocessing/queues.py", line 234, in _feed
    obj = _ForkingPickler.dumps(obj)
  File "/home/farmer/anaconda3/envs/general/lib/python3.6/multiprocessing/reduction.py", line 51, in dumps
    cls(buf, protocol).dump(obj)
AttributeError: Can't pickle local object 'make_func_glob.<locals>.dynamic_func'

所以 python 仍然认为它是一个本地对象,即使我将它添加到 'globals' 字典中。像这样的“全局”想法就可以了,我不需要任何花哨的东西。为了方便起见,我只是动态创建这些功能。如果它们成为全局对象,我会非常高兴。它们将始终由模块定义,其中只有一堆具有几乎相同的定义,因此以编程方式定义它们比手动将它们全部写出来更方便。所以我认为有可能以某种方式让 python 将它们识别为“真正的”函数,就像我通过“exec”定义它们一样。或者至少接近到可以在我的并行代码中使用它们。

最佳答案

正如错误消息所暗示的那样,它更多地与 pickle 有关,而不是动态生成的函数。来自 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor

only picklable objects can be executed and returned.

来自https://docs.python.org/3/library/pickle.html#what-can-be-pickled-and-unpickled , 可以 pickle 的函数种类:

functions defined at the top level of a module (using def, not lambda)

这表明其他类型的函数不能被 pickle。从问题的代码中跳出一个不符合此要求的函数:dynamic_func from...

def make_func(a):
    def dynamic_func(i):
        return i, i**2 + a
    return dynamic_func

...你暗示这就是问题....

So I would have thought it was possible to somehow get python to recognise them as "true" functions

你可以!您可以将 dynamic_func 放在顶层,并使用 partial 而不是闭包...

from functools import partial
def dynamic_func(a, i):
    return i, i**2 + a

def make_func(a):
    return partial(dynamic_func, a)

所以完整...

import concurrent.futures
from functools import partial

def dynamic_func(a, i):
    return i, i**2 + a

def make_func(a):
    return partial(dynamic_func, a)

f_dyns = [make_func(a) for a in range(10)]
def loopfunc(i):
    return f_dyns[i](i)


class Test:
    def __init__(self, myfunc):
        self.f = myfunc

    def loopfunc(self, i):
        return self.f(i)

    def run(self):
        with concurrent.futures.ProcessPoolExecutor(3) as executor:
            for i,r in executor.map(self.loopfunc, range(10)):
                print(i,":",r)

o2 = Test(make_func(1))
o2.run()

但是...为什么没有类的原始形式有效,我不知道。根据我的理解,它会尝试 pickle 一个非顶级函数,所以我认为我的理解是有缺陷的。

关于python - 使用 concurrent.futures.ProcessPoolExecutor 动态创建函数的限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49899351/

相关文章:

python - 在 Python 中,使用 argparse,只允许正整数

python - 如何使用python的多处理终止进程

c++ - 如何为 OpenCV 多核图像处理创建 TBB 任务调度程序? C++

python - 如何将多个 .txt 文件 pickle 成一个 pickle

Python:为什么要 pickle ?

python - 无法加载 pickle 对象

python - 谷歌应用引擎和谷歌云 sql 上的 Flask SqlAlchemy

java - 自动与闭源 Windows XP 基于 GUI 的程序进行交互

Python Pandas xlsxwriter 堆积折线图类型设置Excel中的标准折线图

多线程或多处理