is decorator the right thing to use?
George Sakkis
george.sakkis at gmail.com
Thu Sep 25 16:07:53 EDT 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