Python 对使用concurrent.futures.ProcessPoolExecutor动态创建的函数的限制

Python 对使用concurrent.futures.ProcessPoolExecutor动态创建的函数的限制,python,multiprocessing,pickle,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)

我正在尝试对我在其他函数中动态创建的函数进行多处理。如果提供给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
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'
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'
但是,如果多处理是由类函数启动的,则无法执行此操作:

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()
输出:

0 : 0
1 : 2
2 : 6
3 : 12
4 : 20
5 : 30
6 : 42
7 : 56
8 : 72
9 : 90
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'
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'
输出:

0 : 0
1 : 2
2 : 6
3 : 12
4 : 20
5 : 30
6 : 42
7 : 56
8 : 72
9 : 90
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'
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'
测试:(3,10)
回溯(最近一次呼叫最后一次):
文件“/home/farmer/anaconda3/envs/general/lib/python3.6/multiprocessing/queues.py”,第234行,在
obj=_ForkingPickler.dumps(obj)
文件“/home/farmer/anaconda3/envs/general/lib/python3.6/multiprocessing/reduce.py”,第51行,转储
cls(buf,协议).dump(obj)
AttributeError:无法pickle本地对象“make_func\u glob..dynamic_func”

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

正如错误消息所表明的,这更多地与pickle有关,而不是动态生成的函数。从

只能执行和返回可拾取的对象

从中可以看出,函数的种类如下:

在模块顶层定义的函数(使用def,而不是lambda)

这意味着其他类型的函数不能被酸洗。从问题中的代码中,跳出一个不符合此条件的函数:
dynamic\u func
From

def make_func(a):
    def dynamic_func(i):
        return i, i**2 + a
    return dynamic_func
…你暗示这就是问题所在

因此,我认为有可能以某种方式让python将它们识别为“真正的”函数

你可以!您可以将
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,因此我认为我的理解是有缺陷的。

答案不能解决所有问题。例如:

def _picklable_func(func, *args, **kwargs):
    myfunc = partial(func, *args)
    return myfunc

def invoke_func():
    mod_fun_str = "oh.mymodule.myfunc"
    mod_name, func_name = mod_fun_str.rsplit('.', 1)
    mod = importlib.import_module(mod_name)
    func = getattr(mod, func_name)
    future = self.threadpool.submit(func, *args, **kwargs) # feature.result() always return None
    #future = self.threadpool.submit(_picklable_func(func, *args, **kwargs),  **kwargs) # this doesn't work too, 'myfunc' always returned None


 if __name__=="__main__":
    invoke_func()
问题是函数invoke_func中的future.result()仍然始终返回None

 # myfunc in oh.mymodule  module is 
 def myfunc():
    return "hello"

啊,太好了,谢谢!这个“局部”工具看起来非常有用,我必须记住这一点。虽然这段代码可能会解决这个问题,但它如何以及为什么解决这个问题将真正有助于提高您的帖子质量,并可能导致更多的投票。请记住,你是在将来回答读者的问题,而不仅仅是现在提问的人。请在您的答案中添加解释,并说明适用的限制和假设。虽然此链接可以回答问题,但最好在此处包含答案的基本部分,并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能无效。-我想我错了,我的代码运行得很好,我错过了装饰器中的返回语句