Validate Arguments Passed to a Function¶
Date published: 2018-03-01
Category: Python
Subcategory: Beginner Concepts
Tags: functions, tuples, debugging
The Importance of Evaluating Argument Functions¶
In Python, we often pass objects to our functions - be it variables, tuples or lists.
Before we compute the logic of our function, it's often a good idea to first validate that the arguments passed to the function meet our criteria.
For example, if we had a function that calculated the area of a square, we can't compute the area given a side lenght of -2. Shapes can't have negative side lengths, otherwise they wouldn't be a shape.
The evaluation of our arguments before going into the main logic of our function is a pattern called a guardian. The first three coditions act as guardians, protecting the code from errors not unique to our main logic.
Example - Validate Arguments to a Function¶
Let's start with a function we built in another tutorial to calculate the slope of a line when given two coordinate points.
In this example, we'll validate the conditions that:
Each coordinate point is in a collection that's either a
list
ortuple
.Check there are two coordinate points - equivalent to an x and y coordinate, in each data structure structure.
The values in that data structure are real numbers - so either an
int
orfloat
.
Our initial function¶
def slope_of_line(point_one, point_two):
"""
Calculate slope of a line given two points
Keyword arguments:
point_one -- tuple of two numeric values
point_two -- tuple of two numeric values
"""
x_index = 0
y_index = 1
numerator = point_two[y_index] - point_one[y_index]
denominator = point_two[x_index] - point_one[x_index]
slope = numerator / denominator
return slope
New concepts to use in our validation¶
conditional expressions¶
Typically we say if-else statements span multiple lines. However, we can often write them in one line with a conditional expression.
Here's an example of a conditional statement:
number_status = None
if 5 > 2:
number_status = True
else:
number_status = False
number_status
True
Here's that same statement as a conditional expression
number_status = 5 > 2
number_status
True
In this conditional expression, number_status
is assigned True
if the condition is met; otherwise it's assigned False
.
isinstance
function¶
In Python, we can utilize the built-in isinstance
function to check an object's type.
Here's an example in which we check if "hi"
is a string. Since it is a string, the isinstance
function will return True
.
isinstance("hi", str)
True
We can pass a tuple to the second argument of isinstance
to check for multiple types. As long as our coordinate point is at least one of those types, isinstance
will evaluate to True.
isinstance(3, (int, str))
True
We're returned True
because 3
is of type int
- an integer
len
function¶
We can use the Python built-in len()
function to calculate number of items in a tuple
or list
.
len([3, 2])
2
We're returned 2
because there are two items in the list above
all
function¶
The Python built-in all
function. It returns True
if all boolean values are True in the sequence; otherwise, it returns False
.
Here's all True
values in a list.
all([True, True])
True
As expected, we're returned True
.
And if there's a False
value in a list...
all([True, False])
False
list comprehensions¶
Generate a new list in one line
[x+5 for x in (3,2)]
[8, 7]
We looped through the tuple of (3, 2)
and added 5 to each item. We're returned a new list.
Create function to evaluate necessary conditions¶
def evaluate_arguments_to_calculate_slope(point):
"""
Evaluate three conditions of point to see if we can later use this point to calculate the slope of a line
Keyword arguments:
point -- tuple or list of x-y coordinates of a point
"""
precondition_statuses = []
# validate each data structure is a list or tuple
condition_status = isinstance(point, (tuple, list))
precondition_statuses.append(("tuple or list data structure", condition_status))
# validate there are two values in that data structure
condition_status = len(point) == 2
precondition_statuses.append(("two values in data structures", condition_status))
'''
Validate the two values in that data struxture are floats or ints.
Create a list comprehension to create a new list of two Boolean values.
Logic returns True if the value is a float or int and False if neither data type
'''
digit_statuses = [isinstance(value, (float, int)) for value in point]
# returns True if both items in list are boolean True values; otherwise, returns False
condition_status = all(digit_statuses)
precondition_statuses.append(("ints or floats", condition_status))
return precondition_statuses
We can validate this function works.
evaluate_arguments_to_calculate_slope([3, 2])
[('tuple or list data structure', True), ('two values in data structures', True), ('ints or floats', True)]
[3, 2]
meets all our condtions so we see 3 True
value.
evaluate_arguments_to_calculate_slope([3, "hi"])
[('tuple or list data structure', True), ('two values in data structures', True), ('ints or floats', False)]
[3, "hi"]
doesn't meet all our conditions since "hi"
is a string, not a float
or int
.
Programatically analyze condition results¶
We can automate the manual analysis to see the returned boolean values of our evaluate_arguments_to_calculate_slope
function.
def all_argument_conditions_met(condition_results):
"""
Evalute booleans of conditions
Keyword arguments:
condition_results -- list of tuples of (condition name, boolean status)
"""
conditions_pass = True
for condition in condition_results:
if condition[1] is False:
conditions_pass = False
return conditions_pass
all_argument_conditions_met([('tuple or list data structure', True), ('two values in data structures', True), ('ints or floats', False)])
False
Our all_argument_conditions_met
evaluates to False
because one condition was False
Put All Our Functions Together¶
def slope_of_line(point_one, point_two):
"""
Calculate slope of a line given two points
Keyword arguments:
point_one -- tuple of two numeric values
point_two -- tuple of two numeric values
"""
return_value = None
point_one_status = all_argument_conditions_met(evaluate_arguments_to_calculate_slope(point_one))
point_two_status = all_argument_conditions_met(evaluate_arguments_to_calculate_slope(point_two))
if point_one_status is True and point_two_status is True:
x_index = 0
y_index = 1
numerator = point_two[y_index] - point_one[y_index]
denominator = point_two[x_index] - point_one[x_index]
slope = numerator / denominator
return_value = slope
else:
return_value = "We can't calculate the slope because one of our pre-conditions was not met"
return return_value
Test Function with Various Point Combinations¶
point_one = (1, 1)
point_two = (2, 3)
point_three = (5, 5)
print(slope_of_line(point_one, point_two)) # should return 2
2.0
print(slope_of_line(point_one, point_three)) # should return 1
1.0
print(slope_of_line(point_two, point_three)) # should return 2/3 or 0.67
0.6666666666666666