Re: decorator and API

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Gerard flanagan

    Re: decorator and API

    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 ....
    >
    >
    >
    import random
    >
    def weight(value):
    def set_weight(meth od):
    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.startswi th('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.randrang e(total)
    >
    while choiceweights[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.
    >
    Here is another approach:

    8<-------------------------------------------------------------------

    import random
    from bisect import bisect

    #by George Sakkis
    def take_random_act ion(obj, actions, weights):
    total = float(sum(weigh ts))
    cum_norm_weight s = [0.0]*len(weights)
    for i in xrange(len(weig hts)):
    cum_norm_weight s[i] = cum_norm_weight s[i-1] + weights[i]/total
    return actions[bisect(cum_norm _weights, random.random() )](obj)

    class randomiser(obje ct):

    _cache = []

    @classmethod
    def alert(cls, func):
    assert hasattr(func, 'weight')
    cls._cache.appe nd(func)

    @classmethod
    def register(cls, name, obj):
    actions = {}
    weights = []
    for klass in obj.__class__._ _mro__:
    for val in klass.__dict__. itervalues():
    if hasattr(val, '__name__'):
    key = val.__name__
    if key in actions:
    continue
    elif val in cls._cache:
    actions[key] = val
    weights.append( val.weight)
    actions = actions.values( )
    #setattr(cls, name, classmethod(lam bda cls:
    random.choice(a ctions)(obj)))
    setattr(cls, name, classmethod(lam bda cls:
    take_random_act ion(obj, actions, weights)))

    def randomised(weig ht):
    def wrapper(func):
    func.weight = weight
    randomiser.aler t(func)
    return func
    return wrapper

    class A(object):

    @randomised(20)
    def foo(self):
    print 'foo'

    @randomised(10)
    def bar(self):
    print 'bar'

    class B(A):

    @randomised(50)
    def foo(self):
    print 'foo'

    8<-------------------------------------------------------------------

    randomiser.regi ster('a', A())
    randomiser.regi ster('b', B())
    print 'A'
    randomiser.a()
    randomiser.a()
    randomiser.a()
    randomiser.a()
    randomiser.a()
    randomiser.a()
    print 'B'
    randomiser.b()
    randomiser.b()
    randomiser.b()
    randomiser.b()
    randomiser.b()
    randomiser.b()


Working...