# Validate Arguments Passed to a Function

- March 1, 2018
- Key Terms: 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:

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¶

```
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
```

Here's that same statement as a **conditional expression**

```
number_status = 5 > 2
```

```
number_status
```

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)
```

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))
```

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])
```

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])
```

As expected, we're returned `True`

.

And if there's a `False`

value in a list...

```
all([True, False])
```

##### list comprehensions¶

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.

#### 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])
```

`[3, 2]`

meets all our condtions so we see 3 `True`

value.

```
evaluate_arguments_to_calculate_slope([3, "hi"])
```

`[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)])
```

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
```

```
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
```