decorator and API

Aaron "Castironpi" Brady castironpi at gmail.com
Wed Sep 17 19:09:41 EDT 2008


On Sep 17, 4:56 pm, Lee Harr <miss... at hotmail.com> wrote:
> I have a class with certain methods from which I want to select
> one at random, with weighting.
>
> The way I have done it is this ....
>
> import random
>
> def weight(value):
>     def set_weight(method):
>         method.weight = value
>         return method
>     return set_weight
>
> class A(object):
>     def actions(self):
>         'return a list of possible actions'
>
>         return [getattr(self, method)
>                     for method in dir(self)
>                     if method.startswith('action_')]
>
>     def action(self):
>         'Select a possible action using weighted choice'
>
>         actions = self.actions()
>         weights = [method.weight for method in actions]
>         total = sum(weights)
>
>         choice = random.randrange(total)
>
>         while choice> weights[0]:
>             choice -= weights[0]
>             weights.pop(0)
>             actions.pop(0)
>
>         return actions[0]
>
>     @weight(10)
>     def action_1(self):
>         print "A.action_1"
>
>     @weight(20)
>     def action_2(self):
>         print "A.action_2"
>
> a = A()
> a.action()()
>
> The problem I have now is that if I subclass A and want to
> change the weighting of one of the methods, I am not sure
> how to do that.
>
> One idea I had was to override the method using the new
> weight in the decorator, and then call the original method:
>
> class B(A):
>     @weight(50)
>     def action_1(self):
>         A.action_1(self)
>
> That works, but it feels messy.
>
> Another idea was to store the weightings as a dictionary
> on each instance, but I could not see how to update that
> from a decorator.
>
> I like the idea of having the weights in a dictionary, so I
> am looking for a better API, or a way to re-weight the
> methods using a decorator.
>
> Any suggestions appreciated.
>
> _________________________________________________________________
> Explore the seven wonders of the worldhttp://search.msn.com/results.aspx?q=7+wonders+world&mkt=en-US&form=QBRE

What about a function, 'reweight', which wraps the original, and sets
a weight on the wrapper?

def reweight(value):
    def reset_weight(method):
        #@wraps(method) #optional
        def new_method( *ar, **kw ):
            return method( *ar, **kw )
        new_method.weight = value
        return new_method
    return reset_weight

Call like this:

class B(A):
    action_1= reweight( 50 )( A.action_1 )

You could pass them both in to reweight with two parameters:

class B(A):
    action_1= reweight( 50, A.action_1 )

It's about the same.  Variable-signature functions have limits.

Otherwise, you can keep dictionaries by name, and checking membership
in them in superclasses that have them by hand.  Then you'd need a
consistent name for the dictionary.  That option looks like this
(unproduced):

class A:
   __weights__= {}
   @weight( __weights__, 10 ) ...
   @weight( __weights__, 20 ) ...

class B( A ):
   __weights__= {} #new instance so you don't change the original
   @weight( __weights__, 50 ) ...

B.__weight__ could be an object that knows what it's overriding
(unproduced):

class A:
   weights= WeightOb() #just a dictionary, mostly
   @weights( 10 ) ...
   @weights( 20 ) ...

class B( A ):
   weights= WeightOb( A.weights ) #new, refs "super-member"
   @weights( 50 ) ...

If either of the last two options look promising, I think I can
produce the WeightOb class.  It has a '__call__' method.



More information about the Python-list mailing list