"Fuzzy" hashtables in Python to replace elif chains?
Question: The Pythonic way to choose one of many execution paths or bits of data
dependent on a variable is to use a hashtable, like so:

    
    
    mapping = {'bar':"xyzzy", 'foo':"plugh", 'baz':"frobnitz"}
    magic = mapping[command]
    

What do I do in the case where I want to choose an execution path based on
whether or not a value is within one of many ranges of values?

Or, to put it more succinctly, how do I pythonicize this:

    
    
        #A value from one to four indicates an O-type star. 4/58 chance.
        if typeselec <= 4:
            self.specttype="O"
        #A value from 5-20 indicates a B-type star. 15/58 chance.
        elif typeselec <= 20:
            self.specttype="B"
        #A value from 20-30 indicates an A-type star. 10/58 chance.
        elif typeselec <= 30:
            self.specttype="A"
        #A value from 31-36 indicates an F-type star. 6/58 chance.
        elif typeselec <= 36:
            self.specttype="F"
        #A value from 37-40 indicates a G-type star. 4/58 chance.
        elif typeselec <= 40:
            self.specttype="G"
        #A value from 41-50 indicates a K-type star. 10/58 chance.
        elif typeselec <= 22:
            self.specttype="K"
        #A value from 50-58 indicates an M-type star. 8/58 chance.
        else:
            self.specttype="M"
    

One obvious means would be `{1:'O', 2:'O',3:'O',4:'O',5:'B',6:'B'...}`, but
that seems awfully inelegant and inefficient. Is there any other way?


Answer: Exactly this kind or problem is used as example for the
[bisect](https://docs.python.org/3.4/library/bisect.html#other-examples)
module.

Adapted to your problem it would look like this:

    
    
    from bisect import bisect_left
    
    def select_type(typeselec,
                    breakpoints=[4, 20, 30, 36, 40, 50],
                    types='0BAFGKM'):
        return types[bisect_left(breakpoints, typeselec)]
    
    
    for i in range(59):
        print(i, select_type(i))
    

The difference to the example from the doc is that here the breakpoint is part
of the lower bound (`<=`), where as in the example it's part of the upper
(`>=`), so `bisect_left` needs to be used instead of `bisect` (which is an
alias for `bisect_right`).

Altough `bisect` doesn't have a time complexity of `O(1)`, it used binary
search, so it has a time complexity of `O(log(n))`, which for larger `n` would
be a good improvement of the `O(n)` of the `if` cascade.

