Get a random event out of a set of mutually exclusive events Task:
  
  Get a random event from a set of collectively exhaustive and mutually
  exclusive events, with this set being represented by a list of tuples
  (name, probability). 
  
  Reminder:
  
  
  Collectively exhaustive events means at least one of them must occur.
  Mutually exclusive events means at most 1 of them occurs.
  Collectively exhaustive and mutually exclusive means their probabilities sum up to 1 and only one of them occurs.
  


Code:

Events definition:

events = [
    ('Win', 0.5),
    ('Loss', 0.3),
    ('Draw', 0.2)
]


Getting the random event:

def get_random_event(events):
    random_number = uniform(0., 1.)
    lower_bound = 0
    for event, prob in events:
        if lower_bound &lt;= random_number &lt;= lower_bound + prob:
            return event
        else:
            lower_bound += prob


Illustration:



0 -- -- Win -- -- 0.5 -- Loss -- 0.8 - Draw - 1


Generate a random number in [0,1] and check where it falls.
Some things:


The collectively exhaustive events are not going to change or be updated. So use a tuple instead of a list:

events = (  # &lt;&lt;- Note: ( not [ 
    ('Win', 50),
    ...
)

You don't show your import of random. But I'd prefer that you not import uniform by name, and rather spell it out inside your function. You're only calling it once, so it's more efficient to the reader for you to simply say:

random_number = random.uniform(...)


than to have them search backwards for where uniform occurs. Also, of course, in this context random.uniform actually reads better than just uniform.
You have an input parameter events. You use event, prob in your loop. But event is the singular of events, so when you say return event it seems like you are returning a member of the events iterable. I'd choose a different name, like outcome, making it clear that your returning a component of the inner tuple.
You compare lower_bound &lt;= random_number &lt;= lower_bound + prob but you don't have to. Since your random number is pinned to 0., you could just use a comparison against the high value and get the same result.
Structurally, the lower_bound += prob shouldn't be in the else branch. Yes, it only executes when the if is not executed, but the if should not be part of the flow for that statement. I'd rather see:

if ...
    return ...

lower_bound += prob


This kind of thing comes back to bite you when you're refactoring. Or, worse yet, when someone else is refactoring. (Imagine you changed the return to a print, or something. Suddenly the code stops working.)
The Task description would (almost) make a good function docblock. Note: s/list/iterable/:

from typing import Iterable, Tuple

Name = str
Prob = float

def get_random_event(events: Iterable[Tuple[Name, Prob]]) -&gt; Name:
    """Get a random event from a set of collectively exhaustive 
    and mutually exclusive events, with this set being represented
    by an iterable of tuples (name, probability). 

    """