is decorator the right thing to use?

Bruno Desthuilliers bruno.42.desthuilliers at websiteburo.invalid
Thu Sep 25 10:50:04 CEST 2008


Dmitry S. Makovey a écrit :
> Aaron "Castironpi" Brady wrote:
>  
>> It might help to tell us the order of events that you want in your
>> program.  You're not using 'mymethod' or 'mymethod2', and you probably
>> want 'return fnew' for the future.  Something dynamic with __getattr__
>> might work.  Any method call to A, that is an A instance, tries to
>> look up a method of the same name in the B instance it was initialized
>> with.
> 
> well 'mymethod' and 'mymethod2' were there just to show that A doesn't
> function as a pure proxy - it has methods of it's own. See my respnse to
> Steve - I proxy messages to more than one aggregated object. going over
> them on __getattr__ to look up methods just doesn't seem to be really
> efficient to me (I might be wrong though). Decorators seemed to present
> good opportunity to simplify the code (well except for the decorator
> function itself :) ), make code bit more "fool-proofed" (and give me the
> opportunity to test decorators in real life, he-he).
> 
> So decorators inside of B just identify that those methods will be proxied
> by A. On one hand from logical standpoint it's kind of weird to tell class
> that it is going to be proxied by another class,

Indeed - usually, proxied objects shouldn't have to be aware of the 
fact. That doesn't mean your variation on the proxy pattern is 
necessarily bad design (hard to tell without lot of context anyway...), 
but still there's some alarm bell ringing here IMHO - IOW : possibly the 
right thing to do, but needs to be double-checked.

> but declaration would be
> real close to original function definition which helps to identify where is
> it used.
> 
> Note that my decorator doesn't change original function - it's a subversion
> of decorator to a certain degree as I'm just hooking into python machinery
> to add methods to A upon their declaration in B (or so I think).

I wouldn't call this a "subversion" of decorators - it's even a pretty 
common idiom to use decorators to flag some functions/methods for 
special use.

Now I'm not sure I really like your implementation. Here's a possible 
rewrite using a custom descriptor:

class Proxymaker(object):
     def __init__(self, attrname):
         self.attrname = attrname

     def __get__(self, instance, cls):
         def _proxied(fn):
             fn_name = fn.__name__
             def delegate(inst, *args, **kw):
                 target = getattr(inst, self.attrname)
                 #return fn(target, *args,**kw)
                 method = getattr(target, fn_name)
                 return method(*args, **kw)

             delegate.__name__ = "%s_%s_delegate" % \
                 (self.attrname, fn_name)

             setattr(cls, fn_name, delegate)
             return fn

         return _proxied

class A(object):
     def __init__(self,b):
         self.val='aval'
         self.b=b
         b.val='aval'

     proxy2b = Proxymaker('b')

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

     def mymethod2(self,a):
         print "A::another method, ",a

class B(object):
     def __init__(self):
         self.val='bval'

     @A.proxy2b
     def bmethod(self,a):
         print "B::bmethod"
         print a, self.val

     @A.proxy2b
     def bmethod2(self,a):
         print "B::bmethod2"
         print a, self.val


My point is that:
1/ you shouldn't have to rewrite a decorator function - with basically 
the same code - for each possible proxy class / attribute name pair combo
2/ making the decorator an attribute of the proxy class makes 
dependencies clearer (well, IMHO at least).

I'm still a bit uneasy wrt/ high coupling between A and B, and if I was 
to end up with such a design, I'd probably take some times to be sure 
it's really ok.

My  cents...



More information about the Python-list mailing list