2017-10-06 146 views
6

我在尝试实施新的defaultdict代理对象时遇到了一些麻烦。 documentation有点恐慌,所以我不知道如何正确地做这件事。Subclassing multiprocessing.managers.BaseProxy

我想将defaultdict添加到Manager实例可用的类型列表中。您不能使用的股票multiprocessing.ManagerManager.register方法,所以我做了我自己的存根经理从multiprocessing.mangers.BaseManager

class Manager(BaseManager): 
    pass 

然后我创造了我的multiprocessing.managers.BaseProxy子类来容纳defaultdict(我也开始尝试有花药存根这将子类均defaultdictBaseProxy但似乎并没有工作这是我目前有:。

class ProxyDefaultDict(BaseProxy): 
    def __init__(self, default_factory=None, *args, **kwargs): 
     self.__data = defaultdict(default_factory) 
     super().__init__(*args, **kwargs) 

    def _callmethod(self, methodname, args=(), kwds={}): 
     return getattr(self.__data, methodname)(*args, **kwds) 

    def _getvalue(self): 
     return self.__data.copy() 

    def __repr__(self): 
     return self.__data.__repr__() 

    def __str__(self): 
     return self.__data.__str__() 

Manager.register('defaultdict', ProxyDefaultDict) 

的最终目标是具有安全共享跨越进程和线程键锁共享字典,他再有一个如何我的图像会被初始化的例子:

if __name__ == '__main__': 
    m = Manager() 
    d = m.defaultdict(m.Lock) 
    with d['named_lock']: 
     print('holding the lock') 

不过,我已经打了几个问题:

  1. BaseManager的一个子类似乎只是initalizable通过上下文管理器即with Manager() as m。在这种情况下,我将使用m = Manager() - 因为multiprocessing.Manager允许。不是世界末日,而是更加好奇为什么会出现这种情况,如果这是一个迹象,我正在做一些不正确的事情。

  2. 子类multiprocessing.managers.BaseManager也menas你松开默认注册值从multiprocessing.Manager。在这种情况下,我需要为我的经理重新注册一个ProxyLock(我也不确定预期的方式)。直接将multiprocessing.Manager划分子类是否安全?

  3. 最后,我的ProxyDefaultDict似乎不允许我干净地覆盖它的__init__。我很厌倦在子类化时不打电话给BaseProxy.__init__。问题是BaseProxy也接受位置参数。我想这种方法是使default_factory参数只是一个键控参数,但是它将预期的接口更改为defaultdict,并让我认为我在这里再次做错了一些事情。其他类型如Manager.Lock似乎能够接受位置参数。

感谢您的任何帮助。

回答

3

查看源代码后,对其进行了一些修改,使我能够毫无问题地获得defaultdict类型的代理(根据创建的DictProxy的内置方式)。

from collections import defaultdict 

from multiprocessing.managers import MakeProxyType, SyncManager 

DefaultDictProxy = MakeProxyType("DefaultDictProxy", [ 
    '__contains__', '__delitem__', '__getitem__', '__len__', 
    '__setitem__', 'clear', 'copy', 'default_factory', 'fromkeys', 
    'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 
    'update', 'values']) 

SyncManager.register("defaultdict", defaultdict, DefaultDictProxy) 
# Can also create your own Manager here, just using built in for simplicity 

if __name__ == '__main__': 
    with SyncManager() as sm: 
     dd = sm.defaultdict(list) 
     print(dd['a']) 
     # [] 

就我个人而言,我发现使用已经提供的工具很方便,甚至不用担心如何自己继承它。

但是,我不认为这将允许您创建您正在寻找的默认锁定方案。多处理锁被设计为仅被继承,并且通常锁不能被腌制,这是通过代理传输数据类型的要求。例如:

from multiprocessing import Lock 

    m = SyncManager() 
    m.start() 
    d = m.defaultdict(Lock) 
    print(d['named_lock']) 
    m.shutdown() 

将引发运行时错误:

RuntimeError: Lock objects should only be shared between processes through inheritance 
+0

啊是啊,看起来像你可以叫''上,但SyncManager'不'Manager',感谢register'。当我尝试使用锁作为默认值时,我会得到一个不同的错误。当试图使用托管锁时,例如'm.defaultdict(m.Lock)'我得到一个'TypeError:由于安全原因'不允许腌制一个AuthenticationString对象'错误。对于非托管锁,我得到'Unserializable message:('#RETURN',)'。在Python 3.4上运行。我会想如果一个锁可以被过氧化,它可以被腌制。 – freebie

+0

是的,我认为3.6只是有一个更新错误/检测方法。如果我尝试使用线程锁,我会得到同样的错误。有一些其他的想法https://stackoverflow.com/questions/17960296/trouble-using-a-lock-with-multiprocessing-pool-pickling-error来解决这个问题。 – CasualDemon

相关问题