Lee Harr wrote:
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()
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.
>
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.
>
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()