2.2 Validation and Error Handling

Controlling Python’s Flow

Input Validation

Input validation is a way to ensure that the input is correct. This is especially important when accepting user input. There are many ways to validate input. The most common way is to use the is functions. These functions are built into Python and return a boolean value.

The typical process is to accept an input value validate its type and then process it. If the input is not correct, you can raise an error or ask the user to input the correct value. If you try to process the input data before validating it, you may get an error, and errors stop the program because Python reads synchronously from top to bottom. That means the program will stop completely.

Below, you can see the different is functions that are available in Python.

Input Validation Definition
str.isalpha() True if all alphabetic
str.isdigit() True if all digits
str.isalnum() True if all alphanumeric
str.isdecimal() True if all 0-9
str.isnumeric() True if all numeric
str.islower() True if all lowercase
str.isupper() True if all uppercase

Some of these are self-explanatory, others are less clearly named. Naming is very important in Python, and you should take naming conventions seriously. Some of these are exemplary as to why. For example, isalpha() is a very clear name. It is easy to understand that it checks if the string is alphabetic. However, isdecimal() is less clear. Conventional English would indicate that this tests if the string includes a float or a decimal such as 2.0. But that is not the case. If it were, it would be named isfloat() (which doesn’t exist because the period will always be evaluated as non-numeric). Instead, isdecimal() checks if the string is a decimal number, such as 2 or 90. It checks if all the characters are numbers (0-9) without any other type of character.

Example

# imagine these are inputs, This won't let me do an input
name = "something"
age = "20"

if name.isalpha():
    print("name is alpha")
else:
    print("name is not alpha")

if age.isdigit():
    print("age is digit")
else:
    print("age is not digit")

if age.isdecimal():
    print("age is decimal")
else:
    print("age is not decimal")
name is alpha
age is digit
age is decimal

Error Handling with Try-Except

Error handling is one of the most critical concepts in programming, particularly when working with user input or data that might not be in the expected format. When we validate input, we should always use try and except blocks to catch errors gracefully. This approach allows us to use type casting and other operations without causing our entire program to crash when something goes wrong.

Why Error Handling is Essential

In data analytics, you’ll frequently encounter messy, inconsistent data. Without proper error handling, a single malformed data point could crash your entire analysis. The try-except mechanism provides a safety net that allows your program to continue running even when it encounters unexpected input.

Consider the scenario of accepting numeric input from a user or reading data from a file. If someone enters “12.5a” instead of “12.5”, attempting to convert this to a float with float("12.5a") would normally crash your program with a ValueError. However, with try-except blocks, you can catch this error, handle it appropriately (perhaps by asking for new input or using a default value), and continue processing.

How Try-Except Works

The try-except structure attempts to execute code in the try block, and if any errors occur, it immediately jumps to the except block instead of crashing. This is called “graceful error handling” because it allows your program to handle problems elegantly rather than terminating abruptly.

Common Error Types in Data Analysis: - ValueError: Occurs when you try to convert invalid data (e.g., int("hello")) - TypeError: Happens when you use an operation on an incompatible data type. - IndexError: Occurs when you try to access a list or string index that doesn’t exist. - KeyError: Happens when you try to access a dictionary key that doesn’t exist.

For a complete list of Python exceptions, see the Python documentation.

Examples

Let’s examine these examples to understand best practices:

Try Except, Example 1 (Good Practice)

try:
    age = "12.0"
    print(age.isnumeric())
    if not age.isnumeric():
        raise ValueError("Age is not numeric")
    print("Age is", age)
except ValueError as e:
    print(e)
False
Age is not numeric

Why this is good: All operations that might fail are contained within the try block. The validation logic and potential error are handled together, ensuring that if an error occurs, it’s caught immediately and handled appropriately.

Try Except, Example 2 (Poor Practice)

try:
    age = "120"
    if not age.isnumeric():
        raise ValueError("Age is not numeric")
except ValueError as e:
    print(e)

print("Age is ", age)
Age is  120

Why this is problematic: The final print statement is outside the try-except block. If an error occurs in the try block, the except block will handle it, but then the program will continue and execute print("Age is ", age) regardless. This could lead to using invalid data that should have been rejected. In data analytics, this could mean proceeding with corrupted or invalid data, leading to incorrect results.

Try Except, Example 3 (Alternative Approach)

try:
    upper = "HELLo"
    if not upper.isupper():
        raise ValueError("Not all uppercase")
    print(f"{upper} is all uppercase")
except Exception as e:
    print(e)
Not all uppercase

Analysis: This example uses Exception as a catch-all, which can be useful but should be used judiciously. It’s generally better to catch specific exceptions when possible, as this makes your error handling more precise and helps with debugging.

For example, we could catch ValueError specifically, which would make it clear that we are only interested in handling value-related issues. Then catch a TypeError to handle any type-related issues separately.

try:
    upper = "HELLo"
    if not upper.isupper():
        raise ValueError("Not all uppercase")
    print(f"{upper} is all uppercase")
except ValueError as e:
    print("ValueError:", e)
except TypeError as e:
    print("TypeError:", e)
except Exception as e:
    print("General Exception:", e)
ValueError: Not all uppercase