On Sep 26, 6:38 pm, "Dmitry S. Makovey" <[EMAIL PROTECTED]> wrote:
> I actually ended up rewriting things (loosely based on George's suggested > code) with descriptors and not using metaclasses or decorators (so much for > my desire to use them). > > With following implementation (unpolished at this stage but already > functional) I can have several instances of B objects inside of A object > and proxy certain methods to one or another object (I might be having a > case when I have A.b1 and A.b2 and passing some methods to b1 and others to > b2 having both of the same class B, maybe even multiplexing). You seem to enjoy pulling the rug from under our feet by changing the requirements all the time :) > This one > seems to be fairly light as well without need to scan instances (well, > except for one getattr, but I couldn't get around it). Maybe I didn't > account for some shoot-in-the-foot scenarios but I can't come up with any. > Last time I played with __getattr__ I shot myself in the foot quite well > BTW :) > > [code snipped] > > class A: > b=None ^^^^^^ you don't need this > def __init__(self,b=None): > self.val='aval' > self.b=b > b.val='aval-b' > > def mymethod(self,a): > print "A::mymethod, ",a > > bmethod = ProxyMethod('b',B.bmethod) Although this works, the second argument to ProxyMethod shouldn't be necessary, it's semantically redundant; ideally you would like to write it as "bmethod = ProxyMethod('b')". As before, I don't think that's doable without metaclasses (or worse, stack frame hacking). Below is the update of my original recipe; interestingly, it's (slightly) simpler than before: #======= usage ======================================== from proxies import Proxy class B(object): def __init__(self, val): self.val = val def bmethod(self,n): print "B::bmethod", self.val, n def bmethod2(self,n,m): print "B::bmethod2", self.val, n, m class C(object): def __init__(self, val): self.val = val def cmethod(self,x): print "C::cmethod", self.val, x def cmethod2(self,x,y): print "C::cmethod2",self.val, x, y cattr = 4 class A(Proxy): # DelegateMap: # Maps each delegate method to the proxy attribute that refers to the # respective delegate object DelegateMap = { 'bmethod' : 'b1', 'bmethod2': 'b2', 'cmethod' : 'c', # do NOT delegate C.cmethod2 #'cmethod2': 'c', } def __init__(self, b1, b2, c): print "init A()" # must call Proxy.__init__ super(A,self).__init__(b1=b1, b2=b2, c=c) def amethod(self,a): print "A::mymethod",a if __name__ == '__main__': a = A(B(10), B(20), C(30)) a.amethod('foo') print "bound proxy calls" a.bmethod('foo') a.bmethod2('bar','baz') a.cmethod('foo') try: a.cmethod2('bar','baz') except Exception, ex: print ex print "unbound proxy calls" A.bmethod(a,'foo') A.bmethod2(a,'bar','baz') A.cmethod(a, 'foo') try: A.cmethod2(a,'bar','baz') except Exception, ex: print ex #======= output ======================================== init A() A::mymethod foo bound proxy calls B::bmethod 10 foo B::bmethod2 20 bar baz C::cmethod 30 foo 'A' object has no attribute 'cmethod2' unbound proxy calls B::bmethod 10 foo B::bmethod2 20 bar baz C::cmethod 30 foo type object 'A' has no attribute 'cmethod2' #====== proxies.py ====================================== class _ProxyMeta(type): def __new__(meta, name, bases, namespace): for methodname in namespace.get('DelegateMap', ()): if methodname not in namespace: namespace[methodname] = _ProxyMethod(methodname) return super(_ProxyMeta,meta).__new__(meta, name, bases, namespace) class _ProxyMethod(object): def __init__(self, name): self._name = name def __get__(self, proxy, proxytype): if proxy is not None: return proxy._get_target_attr(self._name) else: return self._unbound_method def _unbound_method(self, proxy, *args, **kwds): method = proxy._get_target_attr(self._name) return method(*args, **kwds) class Proxy(object): __metaclass__ = _ProxyMeta def __init__(self, **attr2delegate): self.__dict__.update(attr2delegate) def _get_target_attr(self, name): try: delegate = getattr(self, self.DelegateMap[name]) return getattr(delegate, name) except (KeyError, AttributeError): raise AttributeError('%r object has no attribute %r' % (self.__class__.__name__, name)) HTH, George -- http://mail.python.org/mailman/listinfo/python-list