decorator and API
Steven D'Aprano
steven at REMOVE.THIS.cybersource.com.au
Wed Sep 17 23:35:52 EDT 2008
On Thu, 18 Sep 2008 02:26:29 +0430, Lee Harr 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 ....
[snip]
You are coupling the weights, the actions, and the object which chooses
an action all in the one object. I find that harder to wrap my brain
around than a more loosely coupled system. Make the chooser independent
of the things being chosen:
def choose_with_weighting(actions, weights=None):
if weights is None:
weights = [1]*len(actions) # equal weights
# Taken virtually unchanged from your code.
# I hope it does what you want it to do!
assert len(weights) == len(actions)
total = sum(weights)
choice = random.randrange(total)
while choice > weights[0]:
choice -= weights[0]
weights.pop(0)
actions.pop(0)
return actions[0]
Loosely couple the actions from their weightings, so you can change them
independently. Here's a decorator that populates a dictionary with
weights and actions:
def weight(value, storage):
def set_weight(method):
storage[method.__name__] = value
return method
return set_weight
Here's how to use it:
class A(object):
weights = {}
def __init__(self):
self.weights = self.__class__.weights.copy()
@weight(10, weights)
def action_1(self):
print "A.action_1"
@weight(20, weights)
def action_2(self):
print "A.action_2"
The class is now populated with a set of default weights, which is then
copied to the instance. If you want to over-ride a particular weight, you
don't need to make a subclass, you just change the instance:
obj = A()
obj.weights["action_1"] = 30
method = choose_with_weighting(obj.weights.keys(), obj.weights.values())
getattr(obj, method)() # call the method
Hope this helps,
--
Steven
More information about the Python-list
mailing list