我试图将关键字参数传递给
Python的multiprocessing.Pool实例中的map函数.
from multiprocessing import Pool
from functools import partial
import sys
# Function to multiprocess
def func(a, b, c, d):
print(a * (b + 2 * c - d))
sys.stdout.flush()
if __name__ == '__main__':
p = Pool(2)
# Now, I try to call func(a, b, c, d) for 10 different a values,
# but the same b, c, d values passed in as keyword arguments
a_iter = range(10)
kwargs = {'b': 1, 'c': 2, 'd': 3}
mapfunc = partial(func, **kwargs)
p.map(mapfunc, a_iter)
输出是正确的:
0
2
4
6
8
10
12
14
16
18
这是最好的做法(最“pythonic”方式)吗?我觉得:
1)常用游泳池;
2)常用关键字参数;
3)但是像上面我的例子这样的组合使用有点像实现这个目的的“hacky”方式.
最佳答案 如果默认参数很大,则使用partial可能不是最理想的.传递给map的函数在发送给worker时被反复pickle(一次为iterable中的每个参数);通过发送限定名称(因为相同的函数在另一侧定义而不需要传输任何数据)(通常)选择全局Python函数,而部分作为函数的pickle和所有提供的文件进行pickle参数.
如果kwargs都是小基元,就像你的例子那样,这并不重要;发送额外参数的增量成本是微不足道的.但是,如果kwargs很大,比如说,kwargs = {‘b’:[1] * 10000,’c’:[2] * 20000,’d’:[3] * 30000},这是一个令人讨厌的代价.
在这种情况下,您有一些选择:
>在全局级别滚动您自己的功能,就像部分一样,但不同的是泡菜:
class func_a_only(a):
return func(a, 1, 2, 3)
>使用Pool的初始化参数,以便每个工作进程设置一次状态,而不是每个任务一次,这样即使您在基于spawn的环境(例如Windows)中工作,也可以确保数据可用
>使用Managers在所有进程之间共享单个数据副本
可能还有其他一些方法.对于不产生巨大泡菜的参数,Point是,partial是好的,但如果绑定的参数很大,它可以杀死你.
注意:在这种特殊情况下,如果您使用的是Python 3.3,则实际上并不需要部分,并且避免使用dict支持元组可以节省大量的开销.无需添加任何新功能,只需一些导入,您可以替换:
kwargs = {'b': 1, 'c': 2, 'd': 3}
mapfunc = partial(func, **kwargs)
p.map(mapfunc, a_iter)
有:
from itertools import repeat
p.starmap(func, zip(a_iter, repeat(1), repeat(2), repeat(3)))
达到类似的效果.要明确的是,这种“修复”(这两种方法对酸洗大对象都有相同的问题)没有任何问题,这只是一种偶尔有用的替代方法.