Python 通过多重处理传递类似dict的对象。队列使其无法通过属性进行修改
实际上,我不确定标题是否恰当地描述了这个问题。让我看看代码Python 通过多重处理传递类似dict的对象。队列使其无法通过属性进行修改,python,multiprocessing,Python,Multiprocessing,实际上,我不确定标题是否恰当地描述了这个问题。让我看看代码 import os from multiprocessing import JoinableQueue # A dict-like class, but is able to be accessed by attributes. # example: d = AttrDict({'a': 1, 'b': 2}) # d.a is equivalent to d['a'] class AttrDict(dict): def __
import os
from multiprocessing import JoinableQueue
# A dict-like class, but is able to be accessed by attributes.
# example: d = AttrDict({'a': 1, 'b': 2})
# d.a is equivalent to d['a']
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
queue = JoinableQueue()
pid = os.fork()
if pid == 0:
d = AttrDict({'a': 1, 'b': 2})
queue.put(d)
queue.join()
os._exit(0)
else:
d = queue.get()
queue.task_done()
#d = AttrDict(d.items()) #(1)
d.a = 3 #(2)
#d['a'] = 3 #(3)
print d
上面的代码打印出{'a':1,'b':2}
,这意味着(2)不起任何作用
如果将(2)更改为(3)或启用(1),则输出为{'a':3,'b':2}
,这是预期的
当它通过队列时,d
似乎发生了什么事情
使用Python2.7进行测试
解决方案: 正如@kindall和@Blckknght所指出的,原因是
d
被选为一个dict,当它被queue.get()
取消勾选时,self.\uu dict\uuuu=self
魔术没有设置。差异可以通过print d.\uu dict\uu
和print d
来体现
为了恢复魔术,我将方法\uuu setstate\uuu
添加到AttrDict
:
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
def __setstate__(self, state):
self.__dict__ = state
代码现在按预期工作。我的猜测是,由于它是
dict
的子类,因此您的AttrDict
被序列化为dict
。特别是指向自身的\uuuu dict\uuu
可能不会被保留。您可以使用某些神奇的方法自定义序列化;请参阅。这实际上不是一个多处理问题,因为多处理。Queue
使用pickle
对通过它发送的对象进行序列化和取消序列化。问题在于pickle
没有正确保存设置self时获得的“神奇”行为
如果您检查在子进程中得到的对象,您会发现它的\uuu dict\uuu
只是一个普通的字典,其内容与对象本身相同。在对象上设置新属性时,其\uuuu dict\uuu
会更新,但继承的字典self
不会更新。我的意思是:
>>> d = AttrDict({"a":1, "b":2})
>>> d2 = pickle.loads(pickle.dumps(d, -1))
>>> d2
{'a': 1, 'b': 2}
>>> d2.b = 3
>>> d2
{'a': 1, 'b': 2}
>>> d2.__dict__
{'a': 1, 'b': 3}
虽然您可以深入了解pickle
如何工作的细节,并让序列化再次工作,但我认为一种更简单的方法是通过让类重写\uuu getattr\uuuu
、\uu setattr\uuuuuu
和\uuu delattr\uuuuuu>方法来依赖不那么神奇的行为:
class AttrDict(dict):
__slots__ = () # we don't need a __dict__
def __getattr__(self, name): # wrapper around dict.__setitem__, with an exception fix
try:
return self[name]
except KeyError:
raise AttributeError(name) from None # raise the right type of exception
def __delattr__(self, name): # wrapper around dict.__delitem__
try:
del self[name]
except KeyError:
raise AttributeError(name) from None # change exception type here too
__setattr__ = dict.__setitem__ # no special exception rewriting needed here
该类的实例将与您自己的实例一样工作,但可以成功地对其进行pickle和unpickle:
>>> d = AttrDict({"a":1, "b":2})
>>> d2 = pickle.loads(pickle.dumps(d, -1)) # serialize and unserialize
>>> d2
{'a': 1, 'b': 2}
>>> d2.b=3
>>> d2
{'a': 1, 'b': 3}
您的实现更加明确。我会考虑的。非常感谢。