我正在尝试使用我在其他函数中动态创建的函数进行一些多处理。如果提供给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'
另一方面,如果我不在那里使用动态生成的函数,我可以在类函数上运行多处理。这有什么办法吗?我尝试将动态生成的函数添加到&#39; globals&#39;字典,但似乎没有帮助:
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仍然认为它是一个本地对象,即使我将它添加到&#39;全局&#39;字典。这样的事情&#39;全局&#39;想法没问题,我不需要任何想象力。为方便起见,我只是动态创建这些函数。我很高兴他们成为全球性的对象。它们将始终由模块定义,其中只有一堆具有几乎相同的定义,因此以编程方式定义它们更方便,而不是手动将它们全部写出来。所以我认为有可能以某种方式让python将它们识别为&#34; true&#34;功能,就像我通过&#39; exec&#39;定义它们一样。或者至少足够接近我可以在我的并行化代码中使用它们。
答案 0 :(得分:3)
正如错误消息所示,它更多地与酸洗而不是动态生成的功能有关。来自https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor
只能执行和返回可选对象。
和https://docs.python.org/3/library/pickle.html#what-can-be-pickled-and-unpickled,可以腌制的各种函数:
在模块顶层定义的函数(使用def,而不是lambda)
表示其他类型的函数可以被腌制。从问题中的代码中可以看出一个不符合这一要求的函数:dynamic_func
来自......
def make_func(a):
def dynamic_func(i):
return i, i**2 + a
return dynamic_func
......你暗示这就是问题......
所以我认为有可能以某种方式让python将它们识别为&#34; true&#34;功能
你可以!您可以将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()
但是......为什么没有课程的原始形式有效,我不知道。根据我的理解,它会试图挑选一个非顶级函数,所以我认为我的理解是有缺陷的。
答案 1 :(得分:0)
答案不能解决所有条件。例如:
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"