is decorator the right thing to use?

George Sakkis george.sakkis at gmail.com
Thu Sep 25 22:07:53 CEST 2008


On Sep 25, 3:36 pm, "Dmitry S. Makovey" <dmi... at athabascau.ca> wrote:

> Aaron "Castironpi" Brady wrote:
> >> I kept this part of the problem out of this discussion as I'm pretty sure
> >> I can fill those in once I figure out the basic problem of
> >> auto-population of proxy methods since for each class/method those are
> >> going to be nearly identical. If I can autogenerate those on-the-fly I'm
> >> pretty sure I can add some extra-logic to them as well including
> >> signature change where A::bmethod(self,c,x) would become
> >> A::bmethod(self,x) etc.
>
> > Do you want to couple instances or classes together?
>
> It would be nice to have objects of B, C and D classes not knowing that they
> are proxied (as they are used on their own too, not only inside of A
> objects).

I'm not sure if the approach below deals with all the issues, but one
thing it does is decouple completely the proxied objects from the
proxy:

#======== usage
================================================================

from proxies import Proxy

class B(object):
    def __init__(self): self.val = 'bval'
    def bmethod(self,n): print "B::bmethod",n
    def bmethod2(self,n,m): print "B::bmethod2",n,m

class C(object):
    def __init__(self): self.val = 'cval'
    def cmethod(self,x): print "C::cmethod",x
    def cmethod2(self,x,y): print "C::cmethod2",x,y
    cattr = 4

class A(Proxy):
    DelegateMap = {
        'bmethod' : B,
        'bmethod2': B,
        'cmethod': C,
        # do NOT delegate C.cmethod2
        #'cmethod2': C,
        'cattr'   : C,
    }

    def __init__(self, b, c):
        print "init A()"
        # must call Proxy.__init__(*delegates)
        super(A,self).__init__(b,c)

    def amethod(self,a):
        print "A::mymethod",a


if __name__ == '__main__':
    a = A(B(), C())
    a.amethod('foo')

    # test bounded methods
    a.bmethod('foo')
    a.bmethod2('bar','baz')
    a.cmethod('foo')
    try: a.cmethod2('bar','baz')
    except Exception, ex: print ex

    # works for unbound methods too
    A.bmethod(a,'foo')
    A.bmethod2(a,'bar','baz')
    A.cmethod(a, 'foo')
    try: A.cmethod2(a,'bar','baz')
    except Exception, ex: print ex

    # non callable attributes
    print A.cattr

#====== output ==================================
init A()
A::mymethod foo
B::bmethod foo
B::bmethod2 bar baz
C::cmethod foo
'A' object has no attribute 'cmethod2'
B::bmethod foo
B::bmethod2 bar baz
C::cmethod foo
type object 'A' has no attribute 'cmethod2'
4

#======== proxies.py =========================

class _ProxyMethod(object):
    def __init__(self, name):
        self._name = name
        def unbound(proxy, *args, **kwds):
            method = proxy._get_target_attr(name)
            return method(*args, **kwds)
        self._unbound = unbound

    def __get__(self, proxy, proxytype):
        if proxy is not None:
            return proxy._get_target_attr(self._name)
        else:
            return self._unbound


class _ProxyMeta(type):
    def __new__(meta, name, bases, namespace):
        for attrname,cls in namespace.get('DelegateMap',
{}).iteritems():
            if attrname not in namespace:
                attr = getattr(cls, attrname)
                if callable(attr):
                    namespace[attrname] = _ProxyMethod(attrname)
                else:
                    namespace[attrname] = attr
        return super(_ProxyMeta,meta).__new__(meta, name, bases,
namespace)


class Proxy(object):
    __metaclass__ = _ProxyMeta

    def __init__(self, *delegates):
        self._cls2delegate = {}
        for delegate in delegates:
            cls = type(delegate)
            if cls in self._cls2delegate:
                raise ValueError('More than one %s delegates were
given' % cls)
            self._cls2delegate[cls] = delegate

    def _get_target_attr(self, name):
        try:
            cls = self.DelegateMap[name]
            delegate = self._cls2delegate[cls]
            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