Это из-за недостатка дизайна, или, может быть, a bug, на пути Proxy
экземпляры становятся незаселенными. Прямо сейчас, если unpickling код видит, что вы работаете внутри Manager
, это дает вам референт, когда вы unpickle, а не Proxy
:
def RebuildProxy(func, token, serializer, kwds):
'''
Function used for unpickling proxy objects.
If possible the shared object is returned, or otherwise a proxy for it.
'''
server = getattr(process.current_process(), '_manager_server', None)
if server and server.address == token.address:
return server.id_to_obj[token.id][0] # This returns the referent
else:
incref = (
kwds.pop('incref', True) and
not getattr(process.current_process(), '_inheriting', False)
)
return func(token, serializer, incref=incref, **kwds) # This returns the Proxy
Иногда это может быть желательными, но иногда его нет, как это есть в вашем случае. Вы можете работать вокруг него, заменив функцию ReduceProxy
, которая делает unpickling в программе:
import multiprocessing.managers as m
from multiprocessing import process
def RebuildProxyNoReferent(func, token, serializer, kwds):
'''
Function used for unpickling proxy objects.
The Proxy object is always returned.
'''
incref = (
kwds.pop('incref', True) and
not getattr(process.current_process(), '_inheriting', False)
)
return func(token, serializer, incref=incref, **kwds)
m.RebuildProxy = RebuildProxyNoReferent # Monkey-patch it
class Foo(object):
def __init__(self):
self.manager = MyManager()
self.manager.register('Bar', Bar)
self.manager.start()
self.bar = self.manager.Bar()
print(type(self.bar))
self.bar.set_proxy(self.bar)
class Bar(object):
def __init__(self):
self.proxy = None
def set_proxy(self, proxy):
print("got {}".format(type(proxy)))
self.proxy = proxy
class MyManager(m.BaseManager):
pass
test = Foo()
Если вы не хотите, чтобы обезьяна-патч, вы также можете создать подкласс BaseProxy
, хотя его немного больше работы:
import multiprocessing.managers as m
from multiprocessing.managers import BaseProxy
from multiprocessing import process
def RebuildProxyNoReferent(func, token, serializer, kwds):
'''
Function used for unpickling proxy objects.
If possible the shared object is returned, or otherwise a proxy for it.
'''
incref = (
kwds.pop('incref', True) and
not getattr(process.current_process(), '_inheriting', False)
)
return func(token, serializer, incref=incref, **kwds)
class MyProxy(BaseProxy):
_exposed_ = ("set_proxy",)
def set_proxy(self, arg):
self._callmethod('set_proxy', (arg,))
def __reduce__(self):
ret = super(MyProxy, self).__reduce__()
# RebuildProxy is the first item in the ret tuple.
# So lets replace it, just for our proxy.
ret = (RebuildProxyNoReferent,) + ret[1:]
return ret
class Foo(object):
def __init__(self):
self.manager = MyManager()
self.manager.register('Bar', Bar, MyProxy)
self.manager.start()
self.bar = self.manager.Bar()
print(type(self.bar))
self.bar.set_proxy(self.bar)
Извините, я не понял ваш вопрос. Как вы сказали, проблема находится где-то перед 'self.bar.set_proxy (...)', так что вы можете оставить класс 'MyManager()'? – khajvah
Класс MyManager() - это просто подкласс многопроцессорности.BaseManager(), но я добавлю его, если это поможет. – user2909415