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.
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")
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")
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.
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")
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")