is decorator the right thing to use?

George Sakkis george.sakkis at gmail.com
Fri Sep 26 21:57:10 EDT 2008


On Sep 26, 6:38 pm, "Dmitry S. Makovey" <dmi... at athabascau.ca> 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



More information about the Python-list mailing list