Is there a better way to write nested if statements in python?

If you are sure there is no other value could have been set to numeral_sys_1 and numeral_sys_2 variables this is the simplest and cleanest solution.

On the other hand, you have to extend the dictionary with its combinations with available values, if you have any other value than "Hexadecimal", "Decimal" and "Binary"

The logic here is; if variable tuples in dictionary keys are not equal to given variable tuple, .get() method returns "0". If given variable tuple match any key in dictionary thus return value of the matching key.

def convert_what(numeral_sys_1, numeral_sys_2):
    return {
        ("Hexadecimal", "Decimal") : 1, 
        ("Hexadecimal", "Binary") : 2, 
        ("Binary", "Decimal") : 3,
        ("Decimal", "Hexadecimal") : 4,
        ("Binary", "Hexadecimal") : 5, 
        ("Decimal", "Binary") : 6, 
     }.get((numeral_sys_1, numeral_sys_2), 0)

There is also using generator could be a solution. Looks much smarter, but I think hard codded dictionary would faster than using a generator for this simple requirement.


Insert all the valid combinations to a dictionary of tuples, and if the combination is not there, return 0:

def convert_what(numeral_sys_1, numeral_sys_2):
    numeral_dict = {
        ("Hexadecimal", "Decimal"    ) : 1,
        ("Hexadecimal", "Binary"     ) : 2,
        ("Decimal",     "Hexadecimal") : 4, 
        ("Decimal",     "Binary"     ) : 6,
        ("Binary",      "Hexadecimal") : 5,
        ("Binary",      "Decimal"    ) : 3
    }
    return numeral_dict.get((numeral_sys_1, numeral_sys_2), 0)

If you are planning to use the function in a loop, it may be a better idea to define the dictionary outside the function, so it wouldn't be recreated on every call to the function.


While @Aryerez and @SencerH.'s answers work, each possible value of numeral_sys_1 has to be repeatedly written for each possible value of numeral_sys_2 when listing the value pairs, making the data structure harder to maintain when the number of possible values increases. You can instead use a nested dict in place of your nested if statements instead:

mapping = {
    'Hexadecimal': {'Decimal': 1, 'Binary': 2},
    'Binary': {'Decimal': 3, 'Hexadecimal': 5},
    'Decimal': {'Hexadecimal': 4, 'Binary': 6}
}
def convert_what(numeral_sys_1, numeral_sys_2):
    return mapping.get(numeral_sys_1, {}).get(numeral_sys_2, 0)

Alternatively, you can generate the pairs of values for the mapping with the itertools.permutations method, the order of which follows that of the input sequence:

mapping = dict(zip(permutations(('Hexadecimal', 'Decimal', 'Binary'), r=2), (1, 2, 4, 6, 3, 5)))
def convert_what(numeral_sys_1, numeral_sys_2):
    return mapping.get((numeral_sys_1, numeral_sys_2), 0)

In my opinion, this convert_what function itself is not very pythonic. I guess the code which calls this code has a bunch of if statements too and does the converting depending on the return value of convert_what(). I suggest something like this:

First step, make one function for every combination:

def hex_dec(inp):
    return 1234  # todo implement
# do the same for hex_bin, bin_dec, dec_hex, bin_hex, dec_bin

Second step, put the function objects in a dict. Note that there are no () after the function names, because we want to store the function object and not call it yet:

converter_funcs = {
    ("Hexadecimal", "Decimal"): hex_dec,
    ("Hexadecimal", "Binary"): hex_bin,
    ("Binary", "Decimal"): bin_dec,
    ("Decimal", "Hexadecimal"): dec_hex,
    ("Binary", "Hexadecimal"): bin_hex,
    ("Decimal", "Binary"): dec_bin,
}

Third and last step, implement a convert function. The if statement checks if both systems are the same. Then, we get the right function from our dict and call it:

def convert(input_number, from_sys, to_sys):
    if from_sys == to_sys:
        return input_number
    func = converter_funcs[(from_sys, to_sys)]
    return func(input_number)