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.
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
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
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
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
Here's that same statement as a conditional expression
number_status = 5 > 2
In this conditional expression,
number_status is assigned
True if the condition is met; otherwise it's assigned
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
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))
3 is of type
int - an integer
We can use the Python built-in
len() function to calculate number of items in a
2 because there are two items in the list above
The Python built-in
all function. It returns
True if all boolean values are True in the sequence; otherwise, it returns
True values in a list.
As expected, we're returned
And if there's a
False value in a list...
Generate a new list in one line
[x+5 for x in (3,2)]
We looped through the tuple of
(3, 2) and added 5 to each item. We're returned a new list.
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.
[('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
[('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
We can automate the manual analysis to see the returned boolean values of our
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
all_argument_conditions_met([('tuple or list data structure', True), ('two values in data structures', True), ('ints or floats', False)])
all_argument_conditions_met evaluates to
False because one condition was
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
point_one = (1, 1) point_two = (2, 3) point_three = (5, 5)
print(slope_of_line(point_one, point_two)) # should return 2
print(slope_of_line(point_one, point_three)) # should return 1
print(slope_of_line(point_two, point_three)) # should return 2/3 or 0.67