我在尝试使用multiprocessing.Pool.starmap时遇到了一个奇怪的错误。重现错误所需的最少代码如下:
from multiprocessing import Pool
# Ignore the fact that this class is useless as-is, it has more code but it wasn't relevant to the bug
class Coordinate(tuple) :
def __new__(cls, *args):
return tuple.__new__(cls, args)
#Essentially just stores two coordinates
class Move :
def __init__(self, oldPos, newPos) :
self.oldPos = oldPos
self.newPos = newPos
def __str__(self) :
return 'Old pos : ' + str(self.oldPos) + ' -- New pos : ' + str(self.newPos)
#Dummy function to show the problem
def funcThatNeedsTwoParams(move, otherParam) :
print(move)
# Second param ignored, no problem there
p = Pool(2)
moveOne = Move(Coordinate(0, 2), Coordinate(0, 1))
moveTwo = Move(Coordinate(2, 1), Coordinate(3, 0))
moveThree = Move(Coordinate(22345, -12400), Coordinate(153, 2357))
# The numbers are irrelevant, no effect on whether problem shows up or not
moves = [moveOne, moveTwo, moveThree]
paramsForStarmap = [[move, 'other param'] for move in moves]
print(paramsForStarmap)
#Output :
#[[<__main__.Move object at 0x1023d4438>, 'other param'], [<__main__.Move object at 0x1023d4470>, 'other param'], [<__main__.Move object at 0x1023d44a8>
for move in [params[0] for params in paramsForStarmap] :
print(move)
#Output :
#Old pos : (0, 2) -- New pos : (0, 1)
#Old pos : (2, 1) -- New pos : (3, 0)
#Old pos : (22345, -12400) -- New pos : (153, 2357)
p.starmap(funcThatNeedsTwoParams, paramsForStarmap)
#Output :
#Old pos : ((0, 2),) -- New pos : ((0, 1),)
#Old pos : ((22345, -12400),) -- New pos : ((153, 2357),)
#Old pos : ((2, 1),) -- New pos : ((3, 0),)
基本上,我有一对参数对,如下所示:[[move,otherParam],[move,otherParam],...],我打印出每个第一个参数,表明移动有效之前使用星图功能。然后我使用之前创建的池调用starmap函数,并告诉它使用我拥有的参数对。然后,莫名其妙地,每个移动的坐标都成为形式((坐标),)的元组,而不是(坐标)。
我似乎无法弄清楚为什么starmap会改变传递给它的对象的属性,任何帮助都会非常感谢,谢谢。
答案 0 :(得分:2)
这是一个有趣的问题。问题不仅仅在于starmap
。它适用于所有Pool
函数 - apply
,map
等。事实证明,问题根本不在于multiprocessing
。当您挑选/取消Coordinate
类:
>>> c = Coordinate(0,2)
>>> print(c)
(0, 2)
>>> str(pickle.loads(pickle.dumps(c)))
'((0, 2),)'
挑选一个tuple
子类并不像看起来那么简单,事实证明。您可以通过定义修复酸洗过程的__reduce__
方法来修复它:
class Coordinate(tuple):
def __new__(cls, *args):
return tuple.__new__(cls, args)
def __reduce__(self):
return (self.__class__, tuple(self))
现在泡菜很好:
>>> c = Coordinate(0,2)
>>> pickle.loads(pickle.dumps(c))
(0, 2)
您的示例代码也可以正常工作。