2014-10-22 5 views
8

Ho una classe che desidero condividere in modalità di sola lettura con processi figli in un pool, quindi ho preparato un proxy di una classe ma non ha funzionato. Quello che segue è un esempio semplificato del mio problema.Accesso a un attributo di un proxy multiprocessing di una classe

from multiprocessing.managers import BaseManager 

class TestClass: 
    def __init__(self, a): 
     self.a = a 
    def b(self): 
     print self.a 

class MyManager(BaseManager): pass 

MyManager.register('test', TestClass) 

if __name__ == '__main__': 
    manager = MyManager() 
    manager.start() 
    t = TestClass(1) 
    print t.a 
    mt = manager.test(2) 
    mt.b() 
    mt.a 

Quando eseguo questo codice ottengo:

1 
2 
Traceback (most recent call last): 
    File "multiprocess_example_stackexchange.py", line 20, in <module> 
    mt.a 
AttributeError: 'AutoProxy[test]' object has no attribute 'a' 

Sembra che non posso accedere al attributo di un oggetto condiviso direttamente tramite un proxy. L'unico modo è usare un metodo che ottiene l'attributo o sto facendo qualcosa di sbagliato?

risposta

9

I Proxy oggetti utilizzati da multiprocessing.BaseManager e sue sottoclassi normalmente solo esporre metodi dagli oggetti che stanno riferimento a, non attributi. Ora, c'è multiprocessing.Manager().Namespace, che fornisce una sottoclasse Proxyfa fornire l'accesso agli attributi, piuttosto che ai metodi. Siamo in grado di creare la nostra Proxy tipo che eredita da quella, che consente l'accesso a tutti i nostri attributi, così come l'accesso alla nostra funzione b:

from multiprocessing.managers import BaseManager, NamespaceProxy 

class TestClass(object): 
    def __init__(self, a): 
     self.a = a 

    def b(self): 
     print self.a 

class MyManager(BaseManager): pass 

class TestProxy(NamespaceProxy): 
    # We need to expose the same __dunder__ methods as NamespaceProxy, 
    # in addition to the b method. 
    _exposed_ = ('__getattribute__', '__setattr__', '__delattr__', 'b') 

    def b(self): 
     callmethod = object.__getattribute__(self, '_callmethod') 
     return callmethod('b') 

MyManager.register('test', TestClass, TestProxy) 

if __name__ == '__main__': 
    manager = MyManager() 
    manager.start() 
    t = TestClass(1) 
    print t.a 
    mt = manager.test(2) 
    print mt.a 
    mt.a = 5 
    mt.b() 

uscita:

1 
2 
5 

Edit:

Se si desidera essere in grado di aggiungere dinamicamente i metodi dalla classe originale a una classe Proxy, è possibile eseguire una delle seguenti operazioni:

from multiprocessing.managers import BaseManager, NamespaceProxy 
import inspect 

class TestClass(object): 
    def __init__(self, a): 
     self.a = a 

    def b(self): 
     print self.a 

class AnotherClass(object): 
    def __init__(self, a): 
     self.a = a 

    def c(self): 
     print self.a 

class MyManager(BaseManager): pass 

class ProxyBase(NamespaceProxy): 
    _exposed_ = ('__getattribute__', '__setattr__', '__delattr__') 

class TestProxy(ProxyBase): pass 
class AnotherProxy(ProxyBase): pass 


def register_proxy(name, cls, proxy): 
    for attr in dir(cls): 
     if inspect.ismethod(getattr(cls, attr)) and not attr.startswith("__"): 
      proxy._exposed_ += (attr,) 
      setattr(proxy, attr, 
        lambda s: object.__getattribute__(s, '_callmethod')(attr)) 
    MyManager.register(name, cls, proxy) 

register_proxy('test', TestClass, TestProxy) 
register_proxy('another', AnotherClass, AnotherProxy) 

if __name__ == '__main__': 
    manager = MyManager() 
    manager.start() 
    mt = manager.test(2) 
    ma = manager.another(3) 
    mt.b() 
    ma.c() 
    mt.a = 5 
    ma.a = 6 
    mt.b() 
    ma.c() 
+0

grazie mille! –

+0

Cosa succede se ho decine di metodi e una dozzina di attributi? Devo scriverli tutti nella classe TestProxy? O c'è una soluzione più generale? – selotec

+0

@selotec Non ho tempo per fornire un'implementazione, ma dovresti essere in grado di eseguire iterate su tutti gli attributi della classe e aggiungere dinamicamente i metodi trovati alla tua classe Proxy e alla variabile '_exposed' – dano

0

Ecco un'alternativa meno dettagliata che ho trovato che funziona bene nella pratica. Non sono sicuro se ci sono degli svantaggi.

class TestClass: 
    def __init__(self, a): 
     self.a = a 
    def b(self): 
     print self.a 

def wrap_test_class(*args, **kwargs): 
    obj = TestClass(*args, **kwargs) 
    obj.get_a = lambda: obj.a 
    return obj 

class MyManager(BaseManager): pass 

MyManager.register('test', wrap_test_class) 

Ciò consente di accedere a chiamando proxy_object.get_a()