Python Beginner Concepts Tutorial

# Validate Arguments Passed to a Function

### 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:

1) Each coordinate point is in a collection that's either a list or tuple.

2) Check there are two coordinate points - equivalent to an x and y coordinate, in each data structure structure.

3) The values in that data structure are real numbers - so either an int or float.

#### Our initial function¶

In :
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:

In :
number_status = None

if 5 > 2:
number_status = True
else:
number_status = False

In :
number_status

Out:
True

Here's that same statement as a conditional expression

In :
number_status = 5 > 2

In :
number_status

Out:
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.

In :
isinstance("hi", str)

Out:
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.

In :
isinstance(3, (int, str))

Out:
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.

In :
len([3, 2])

Out:
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.

In :
all([True, True])

Out:
True

As expected, we're returned True.

And if there's a False value in a list...

In :
all([True, False])

Out:
False
##### list comprehensions¶

Generate a new list in one line

In :
[x+5 for x in (3,2)]

Out:
[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¶

In :
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.

In :
evaluate_arguments_to_calculate_slope([3, 2])

Out:
[('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.

In :
evaluate_arguments_to_calculate_slope([3, "hi"])

Out:
[('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.

In :
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 is False:
conditions_pass = False
return conditions_pass

In :
all_argument_conditions_met([('tuple or list data structure', True), ('two values in data structures', True), ('ints or floats', False)])

Out:
False

Our all_argument_conditions_met evaluates to False because one condition was False

### Put All Our Functions Together¶

In :
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¶

In :
point_one = (1, 1)
point_two = (2, 3)
point_three = (5, 5)

In :
print(slope_of_line(point_one, point_two))  # should return 2

2.0

In :
print(slope_of_line(point_one, point_three)) # should return 1

1.0

In :
print(slope_of_line(point_two, point_three)) # should return 2/3 or 0.67

0.6666666666666666