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