Enforce user of Python program to pass function arguments in a certain order

Question:

I have a simple python script that calculates if the price of one variable is less than the other. For example, var1 should always be passed to the calculation function first, and var2 should always be passed second. Then, the function needs to check if var1 is less than var2 Currently, for the calculation function, I have positional parameters, and it is on the end user to correctly pass the parameters in the correct order for the calculation. It seems intuitive enough to know what position to pass each parameter in, but I am wondering if there is a way to safeguard against users being sloppy and passing parameters to the wrong positions?

Example code one:

def get_values():
    var_one = 1
    print("Var1: ", var_one)

    var_two = 2
    print("Var2: ", var_two)

    print("")
    
    calculate(var_one, var_two)

def calculate(var_one, var_two):
    if var_one < var_two:
        print("Var1 is less than Var2")
    else:
        print("Var2 is greater than Var1")

if __name__ == "__main__":
    get_values()

Output:

Var1:  1
Var2:  2

Var1 is less than Var2

This is all fine and well. This is the correct way to call the function and it prints logically correct output. However, if I flip the parameter positions in the function call and change the values of var_one and var_two, this happens:

Example Code 2:

def get_values():
    var_one = 3
    print("Var1: ", var_one)

    var_two = 2
    print("Var2: ", var_two)

    print("")
    
    calculate(var_two, var_one)

def calculate(int_one, int_two):
    if int_one < int_two:
        print("Var1 is less than Var2")
    else:
        print("Var2 is greater than Var1")

if __name__ == "__main__":
    get_values()

Output:

Var1:  3
Var2:  2

Var1 is less than Var2

As seen here, when var_one is greater than var_two and when we pass the parameters in the wrong position, the output contains a clear logical error. Looking at the code, Var1 is clearly greater than Var2. While it is intuitive how you need to position the parameters here, is there anything that can be done in the calculate() function signature to safeguard against this kind of human/user error, and to ensure var_one always gets passed first to the function before var_two?

***It is important to note that I am using static values here. However, let’s say I am pulling in dynamic / changing integers from an API, and I always want to make sure value1 is less than value2, then this needs to be enforced.

Asked By: Oxth

||

Answers:

I don’t believe that doing this is possible in Python, or any other language. Since part of the calculate function behaves in a different, but "allowed" way, it can’t tell if the inputs have been swapped.

As a human, you want to read the name of the variables that they are passing in. The issue is what happens when someone names their variables first_value and second_value? How do you know which one goes first? Or if they say initial_number and updated_number? The function can’t read the mind of the programmer to determine what variable they felt to be "one" or "two"

One solution to this is providing a good docstring to make sure that the user knows how your function should be called. The pycharm way to do it is below.

def calculate(int_one, int_two):
    """
    Determines which of two numbers is greater (fill with info about what it actually does)
    :param int_one: The first number. Must be the first value received.
    :param int_two: The second number. Must be the second value received.
    :return: Nothing
    """
    if int_one < int_two:
        print("Var1 is less than Var2")
    else:
        print("Var2 is greater than Var1")
Answered By: Luke B

One Python solution to this problem is keyword arguments, commonly called kwargs.

For example:

def calculate(**kwargs):
    var1 = kwargs["var1"]
    var2 = kwargs["var2"]
    print("var1 is less" if var1 < var2 else "var1 is not less")

Now you can indicate the var1 and var2 arguments in any order you like when calling calculate(), but you must provide them by name because they are no longer positional. For example:

>>> calculate(var1=1, var2=2)
var1 is less

>>> calculate(var1=1, var2=0)
var1 is not less

>>> calculate(var2=2, var1=1)
var1 is less

The fact that argument names must be supplied makes it obvious to the caller which is which (e.g. src/dest, width/height, angle_degrees/angle_radians).

You now, of course, must deal with the possibility that the caller does not supply var1 and/or var2 and you can deal with that as follows:

def calculate(**kwargs):
    if "var1" not in kwargs:
        raise Exception("var1 argument missing")
    elif "var2" not in kwargs:
        raise Exception("var2 argument missing")

    var1 = kwargs["var1"]
    var2 = kwargs["var2"]
    print("var1 is less" if var1 < var2 else "var1 is not less")

Alternatively, when it makes sense, you can provide default values for missing arguments. For example:

def calculate(**kwargs):
    var1 = kwargs.get("var1", 0)
    var2 = kwargs.get("var2", 100)
    print("var1 is less" if var1 < var2 else "var1 is not less")
Answered By: jarmod
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.