On Sep 27, 9:23 am, George Sakkis <[EMAIL PROTECTED]> wrote: > On Sep 27, 1:44 am, "Dmitry S. Makovey" <[EMAIL PROTECTED]> wrote: > > > I guess my bias is towards more explicit declarations thus > > > bmethod=ProxyMethod('b',B.bmethod) > > > looks more attractive to me, but I stand to be corrected/educated why is > > that not the right thing to do? > > I see where you're coming from and I also prefer explicit reflection > mechanisms instead of strings (e.g. avoid eval/exec as much as > possible). As I mentioned, the second argument to ProxyMethod is (for > all sane purposes) redundant, so if you could implement it in a way > that "bmethod = ProxyMethod('b')" worked, I would be all for it, but > AFAIK it's not possible without a metaclass.
Just for completeness, here's a metaclass version that uses ProxyMethod declarations instead of a dict; you'll probably like this better: #======= usage ========================= from proxies import Proxy, ProxyMethod 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): 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 bmethod = ProxyMethod('b1') bmethod2 = ProxyMethod('b2') cmethod = ProxyMethod('c') 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 attrname,value in namespace.iteritems(): if isinstance(value, ProxyMethod) and value.name is None: value.name = attrname return super(_ProxyMeta,meta).__new__(meta, name, bases, namespace) class ProxyMethod(object): def __init__(self, proxy_attr, name=None): self._proxy_attr = proxy_attr self.name = name def __get__(self, proxy, proxytype): if proxy is not None: return self.__get_target_attr(proxy) else: return self.__unbound_method def __unbound_method(self, proxy, *args, **kwds): method = self.__get_target_attr(proxy) return method(*args, **kwds) def __get_target_attr(self, proxy): try: delegate = getattr(proxy, self._proxy_attr) return getattr(delegate, self.name) except AttributeError: raise AttributeError('%r object has no attribute %r' % (proxy.__class__.__name__, self.name)) class Proxy(object): __metaclass__ = _ProxyMeta def __init__(self, **attr2delegate): self.__dict__.update(attr2delegate) #====================================================== If you want to eliminate completely specifying attributes with strings, it's easy to modify the above so that you write instead: class A(Proxy): ... bmethod = ProxyMethod(lambda self: self.b1) bmethod2 = ProxyMethod(lambda self: self.b2) This is more verbose for the common case, but it's more flexible in cases where the callable may be more complex than a plain getattr(). Actually you can support both, it doesn't have to be either/or; just check whether the argument to ProxyMethod is a callable and if not, make it: from operator import attrgetter class ProxyMethod(object): def __init__(self, proxy_attr, name=None): if not callable(proxy_attr): proxy_attr = attrgetter(proxy_attr) ... Remaining Implementation is left as an exercise to the reader ;) George -- http://mail.python.org/mailman/listinfo/python-list