通过Python多处理修改对象。池:奇怪的行为

通过Python多处理修改对象。池:奇怪的行为,python,multiprocessing,pickle,pool,Python,Multiprocessing,Pickle,Pool,我有一个具有两个属性的对象:dict和int。当我通过multi-processing.Pool使用分叉进程修改对象时,我会用修改后的int属性返回对象,但dict没有修改。为什么呢 from multiprocessing import Pool def fork(): someObject = SomeClass() for i in range(10): someObject.method(i) print("in fork, someOb

我有一个具有两个属性的对象:dict和int。当我通过multi-processing.Pool使用分叉进程修改对象时,我会用修改后的int属性返回对象,但dict没有修改。为什么呢

from multiprocessing import Pool

def fork():
    someObject = SomeClass()
    for i in range(10):
        someObject.method(i)    
    print("in fork, someObject has dct=%s and nbr=%i" % (someObject.dct, someObject.nbr))
    return someObject

def test():
    pool = Pool(processes=1)             
    result = pool.apply(func=fork)
    print("in main, someObject has dct=%s and nbr=%i" % (result.dct, result.nbr))

class SomeClass(object):
    dct = {}
    nbr = 0     
    def method(self, nbr):
        self.dct[nbr]=nbr
        self.nbr+=nbr

if __name__=='__main__':
    test()
输出:

在fork中,someObject具有dct={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9}和nbr=45


主要来说,someObject的dct={}和nbr=45

父进程的
SomeClass.dct
SomeClass.nbt
副本与子进程不同

更新
nbr
而不是
dct
的原因是
nbr
在执行
self.nbr+=nbr
时实际上变成了一个实例变量,它被pickle并发送回父进程。但是您从未将
self.dct
分配给任何对象,因此
self.dct
(实际上是指
SomeClass.dct
)不会被pickle

您可以通过在
SomeClass
上定义
\uu getstate\uuu()
来看到这一点:

class SomeClass(object):
    dct = {}
    nbr = 0
    def method(self, nbr):
        self.dct[nbr]=nbr
        self.nbr+=nbr

    def __getstate__(self):
        res = self.__dict__
        print("pickled", res)
        return res
这张照片是:

in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45})
in main, someObject has dct={} and nbr=45
in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45, 'dct': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}})
in main, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
通过将
dct
分配给“自身”,可以强制对其进行pickle处理:

这张照片是:

in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45})
in main, someObject has dct={} and nbr=45
in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45, 'dct': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}})
in main, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45

父进程的
SomeClass.dct
SomeClass.nbt
副本与子进程不同

更新
nbr
而不是
dct
的原因是
nbr
在执行
self.nbr+=nbr
时实际上变成了一个实例变量,它被pickle并发送回父进程。但是您从未将
self.dct
分配给任何对象,因此
self.dct
(实际上是指
SomeClass.dct
)不会被pickle

您可以通过在
SomeClass
上定义
\uu getstate\uuu()
来看到这一点:

class SomeClass(object):
    dct = {}
    nbr = 0
    def method(self, nbr):
        self.dct[nbr]=nbr
        self.nbr+=nbr

    def __getstate__(self):
        res = self.__dict__
        print("pickled", res)
        return res
这张照片是:

in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45})
in main, someObject has dct={} and nbr=45
in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45, 'dct': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}})
in main, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
通过将
dct
分配给“自身”,可以强制对其进行pickle处理:

这张照片是:

in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45})
in main, someObject has dct={} and nbr=45
in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45, 'dct': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}})
in main, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45

父进程的
SomeClass.dct
SomeClass.nbt
副本与子进程不同

更新
nbr
而不是
dct
的原因是
nbr
在执行
self.nbr+=nbr
时实际上变成了一个实例变量,它被pickle并发送回父进程。但是您从未将
self.dct
分配给任何对象,因此
self.dct
(实际上是指
SomeClass.dct
)不会被pickle

您可以通过在
SomeClass
上定义
\uu getstate\uuu()
来看到这一点:

class SomeClass(object):
    dct = {}
    nbr = 0
    def method(self, nbr):
        self.dct[nbr]=nbr
        self.nbr+=nbr

    def __getstate__(self):
        res = self.__dict__
        print("pickled", res)
        return res
这张照片是:

in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45})
in main, someObject has dct={} and nbr=45
in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45, 'dct': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}})
in main, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
通过将
dct
分配给“自身”,可以强制对其进行pickle处理:

这张照片是:

in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45})
in main, someObject has dct={} and nbr=45
in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45, 'dct': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}})
in main, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45

父进程的
SomeClass.dct
SomeClass.nbt
副本与子进程不同

更新
nbr
而不是
dct
的原因是
nbr
在执行
self.nbr+=nbr
时实际上变成了一个实例变量,它被pickle并发送回父进程。但是您从未将
self.dct
分配给任何对象,因此
self.dct
(实际上是指
SomeClass.dct
)不会被pickle

您可以通过在
SomeClass
上定义
\uu getstate\uuu()
来看到这一点:

class SomeClass(object):
    dct = {}
    nbr = 0
    def method(self, nbr):
        self.dct[nbr]=nbr
        self.nbr+=nbr

    def __getstate__(self):
        res = self.__dict__
        print("pickled", res)
        return res
这张照片是:

in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45})
in main, someObject has dct={} and nbr=45
in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45, 'dct': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}})
in main, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
通过将
dct
分配给“自身”,可以强制对其进行pickle处理:

这张照片是:

in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45})
in main, someObject has dct={} and nbr=45
in fork, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45
('pickled', {'nbr': 45, 'dct': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}})
in main, someObject has dct={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} and nbr=45

我找到了另一个解决办法。我没有使用
dict()
,而是使用了
multiprocessing.Manager.dict()
,它按预期工作。

我找到了另一种解决方案。我没有使用
dict()
,而是使用了
multiprocessing.Manager.dict()
,它按预期工作。

我找到了另一种解决方案。我没有使用
dict()
,而是使用了
multiprocessing.Manager.dict()
,它按预期工作。

我找到了另一种解决方案。我没有使用
dict()
,而是使用了
multiprocessing.Manager.dict()
,它工作正常。

先生,你是最棒的。我花了几个小时试图弄明白这一点。谢谢我想了解为什么
self.dct[nbr]=nbr
不“分配任何东西”。@derekvarren
self.nbr+=nbr
所做的是:1。阅读
self.nbr
,2。向其添加
nbr
,3。将结果分配回
self.nbr
。相比之下,
self.dct[nbr]=nbr
所做的是:1。读取
self.dct
(它是对
dict
对象的引用),2。通过将键
nbr
设置为
nbr
来改变dict的状态。在任何情况下,您实际上都不会将dict分配回
self.dct
;你只是改变了它的状态,先生,你是最伟大的。我花了几个小时试图弄明白这一点。谢谢我想了解为什么
self.dct[nbr]=nbr
不“分配任何东西”。@derekvarren
self.nbr+=nbr
所做的是:1。阅读
self.nbr
,2。向其添加
nbr
,3。将结果分配回
self.nbr
。相比之下,
self.dct[nbr]=nbr
所做的是:1。读取
self.dct
(它是对
dict
对象的引用),2。通过将键
nbr
设置为
nbr
来改变dict的状态。在任何情况下,您实际上都不会将dict分配回
self.dct
;你只是改变了它的状态,先生,你是最伟大的。我花了几个小时试图弄明白这一点。谢谢我想了解为什么
self.dct[nbr]=nbr
不“分配任何东西”。@derekvarren
self.nbr+=nbr
所做的是:1。阅读
self.nbr
,2。向其添加
nbr
,3。将结果分配回
self.nbr
。相比之下,
self.dct[nbr]=nbr
所做的是:1。读取
self.dct
(它是对
dict
对象的引用),2。通过设置键nbr改变dict的状态