2014-10-08 62 views
3

我尝试使用multiprocessing.Pool.starmap时遇到了一个奇怪的错误。重现bug所需的最低代码是在这里:在传入参数之前修改参数?

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),) 

基本上,我有对参数数组,像这样:[[举动,otherParam],[动,otherParam],...] ,我打印出每个第一个参数,以显示在使用starmap函数之前这些移动是有效的。然后我使用先前创建的池调用starmap函数,并告诉它使用我有的参数对。然后,莫名其妙地,每一个动作的坐标变成形式((坐标),)的元组,而不是(坐标)。

我似乎无法弄清楚为什么星图会改变传递给它的对象的属性,任何帮助将不胜感激,谢谢。

回答

2

这是一个有趣的。这个问题不仅仅是starmap。它发生在所有Pool函数 - apply,map等。而事实证明,这个问题根本不在multiprocessing

>>> c = Coordinate(0,2) 
>>> print(c) 
(0, 2) 
>>> str(pickle.loads(pickle.dumps(c))) 
'((0, 2),)' 

酸洗tuple子就没有那么简单,因为它看起来,在事实证明:当你泡菜/ unpickle的Coordinate类它的发生。您可以通过定义一个__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) 

而且你的示例代码工作正常了。

+0

太好了,谢谢你的回答!你是怎么发现酸洗是造成这个问题的原因? – 2014-10-08 15:26:51

+2

@MarcusBuffett嗯,首先我注意到当我使用'map'和'apply'时发生破损,所以我知道它不是'starmap'问题。然后,我在“Pool”代码中添加了打印语句,以跟踪打印“Move”实例的输出,并注意到'Coordinates'被破坏的点一旦到达工作进程。我的下一个想法是当'Coordinate'被腌制时发生了一些奇怪的事情,因为它是一个'tuple'子类,这是一种不寻常的模式。所以我测试了它并确认这是问题。 – dano 2014-10-08 15:33:11

+0

谢谢!最后一个问题,你说你在Pool代码中添加了打印语句,你是怎么做到的?看起来像一个有用的调试技巧 – 2014-10-08 15:38:30