decorator and API

Peter Otten __peter__ at web.de
Thu Sep 18 05:32:47 EDT 2008


Steven D'Aprano wrote:

I agree with you that the simple explicit approach is better.
Now, to answer the question the OP didn't ask:

> 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!

It probably doesn't.

>     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]

Assume two actions with equal weights [1, 1]. total becomes 2, and choice is
either 0 or 1, but never > weights[0].

While this can be fixed by changing the while condition to

while choice >= weights[0]: #...

I prefer an approach that doesn't destroy the actions and weights lists,
something like

import bisect

def choose_with_weighting(actions, weights=None, acc_weights=None):
    if acc_weights is None:
        if weights is None:
            return random.choice(actions)
        else:
            sigma = 0
            acc_weights = []
            for w in weights:
                sigma += w
                acc_weights.append(sigma)
    return actions[bisect.bisect(acc_weights,
random.randrange(acc_weights[-1]))]

especially if you prepare the acc_weights list once outside the function.

Peter




More information about the Python-list mailing list