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