Errors can be both annoying and frustrating. Often, these exceptions occur due to incorrect implementation or usage.
As programmers, it is crucial to anticipate exceptions and handle them correctly. Throughout this lesson, we will explore the most common exceptions encountered and use practical examples to explain the fundamentals of error handling.
Prerequisites
- A working knowledge of Python programming
- Python installed on your system
- A code editor
- A willingness to learn and experiment with Python error-handling
Basics of Try-Except
The try-except
block is used to handle exceptions. Let us take a look at the try
and except
blocks and explore how they work.
Try block
The try block is where you enclose the code that you anticipate might raise an error.
Except block
This block handles the exception.
Try-Except
try:
# Code that may raise an error
except:
# Handle the exception, e.g., print an error message
Handling different types of exceptions
A try block can have several exceptions to handle specific errors. You could also use the Exception
block to generalize every error.
try:
pass
except Exception:
# do something
pass
There are also cases where you want to print the error message instead of a different message in the output.
try:
result = '5' + 2
print(result)
except TypeError as e:
print('error', e)
Result in the terminal:
error can only concatenate str (not "int") to str
How do you know what specific error to handle?
Simply run the code without an error handler; whatever error it throws at the terminal, you want to use it in the except block.
Several errors you could encounter are:
- Syntax error
- NameError
- IndexError
- KeyError
- ZeroDivisionError
- TypeError
- ValueError
- ImportError
- FileNotFoundError
- OsError
- AttributeError
If you want to read more about these errors check the documentation.
Using specific exception types
Step 1: Concatenating a string and an integer without a try-except block.
print('a' + 5)
Result in the Terminal:
print('a' + 5)
TypeError: can only concatenate str (not "int") to str
Step 2: Now that we have identified the exception, let’s use a try-except block.
try:
result = 'a' + 5
print(result)
except TypeError:
print('invalid operation')
Result in the terminal: invalid operation
Using multiple except blocks
In this example, we have a try block that contains code that may raise exceptions. We have multiple exception blocks to handle specific exception types.
try:
# Code that may raise exceptions
num = int(input("Enter a number: "))
result = 10 / num
fruits = ["apple", "banana", "cherry"]
index = int(input("Enter an index to access a fruit: "))
print(f"The selected fruit is: {fruits[index]}")
except ValueError:
# Handle a ValueError (e.g., when user input can't be converted to int)
print("Invalid input. Please enter a valid number.")
except ZeroDivisionError:
# Handle a ZeroDivisionError (e.g., when dividing by zero)
print("Division by zero is not allowed.")
except IndexError:
# Handle an IndexError (e.g., when accessing an index outside the list range)
print("Invalid index. Please choose a valid index.")
except Exception as e:
# Handle other exceptions not explicitly caught above
print(f"An error occurred: {e}")
The else block
The else block is useful for executing code when no exceptions occur, providing a way to separate the normal code flow from exception handling logic.
try:
# Code that may or may not raise an exception
num1 = int(input("Enter the first number: "))
num2 = int(input("Enter the second number: "))
result = num1 / num2
except ZeroDivisionError:
# Handle division by zero exception
print("Division by zero is not allowed.")
except ValueError:
# Handle invalid input (non-integer) exception
print("Invalid input. Please enter valid numbers.")
else:
# Code to execute if no exceptions are raised
print(f"The result of the division is: {result}")
Using the 'finally' block for cleanup
Regardless of whether an exception is raised or not, the code within the finally block is executed.
This can be used for closing files or network connections.
try:
# Code that may or may not raise an exception
file = open("example.txt", "r")
data = file.read()
except FileNotFoundError:
# Handle file not found exception
print("File not found.")
except Exception as e:
# Handle other exceptions
print(f"An error occurred: {e}")
finally:
# Code that always executes, regardless of exceptions
print("Cleaning up resources...")
file.close() # Close the file to release resources
Custom Exception Handling
Raising exceptions with the 'raise' keyword
Raising exceptions is important for signaling errors and exceptional conditions in your code.
def some_function():
# Some condition that warrants raising an exception
raise Exception("This is a custom exception message.")
try:
some_function()
except Exception as e:
print(f"Exception caught: {e}")
Example: Imagine you are writing a program that calculates the square root of a number provided by the user. You want to ensure that the user enters a valid positive number because the square root of negative numbers is undefined in the real number system.
import math
def calculate_square_root(number):
if number < 0:
raise ValueError("Invalid input: Please enter a positive number.")
return math.sqrt(number)
try:
user_input = float(input("Enter a number: "))
result = calculate_square_root(user_input)
print(f"The square root of {user_input} is {result}")
except ValueError as e:
print(f"Error: {e}")
Result 1:
Enter a number: -2
Error: Invalid input: Please enter a positive number.
Result 2:
Enter a number: re
Error: could not convert string to float: 're'
Exercise
In programming you can only familiarize yourself with these concepts through practice, look things up online, and try things out. For the exceptions that weren’t covered in this article, purposely create some issues. For example, if it were a syntax error you could write a function without a colon or maybe a variable without the equal sign. For NameError
you could print out a variable name that doesn’t exist. In the case of an importError
, you could try importing a module that doesn’t exist.
Mistakes to avoid when handling exceptions
- The error message should be specific and easily understood.
try:
user_input = int(input('Enter a number: '))
except ValueError as e:
print('An error has occurred')
Why is it a problem?
What error has occurred? This message is confusing to both the user and the developer. Imagine this was a program with several thousands of lines of code, and you are trying to debug. With this kind of message, it makes debugging harder. Picture a user using this program. They will feel frustrated because the error message doesn’t explain clearly what they did wrong.
Better way to write it:
try:
user_input = int(input('Enter a number: '))
except ValueError as e:
print('Enter a valid integer')
print(e)
- Relying on exceptions to control program flow. For example, using exceptions for if-else logic.
try:
result = num / 2
if num <= 0:
raise ValueError(f'{num} must be greater than zero')
print(result)
except ValueError as e:
print(e)
Why is it a problem?
A simple if-else block would have solved it. Always remember that simplicity is preferable to writing complex code.
Better way to write it:
num = 5
if num > 0:
print(num / 2)
else:
print(f'{num} must be greater than zero')
- Placing large sections of code within a
try
block.
try:
num1 = int(input("Enter the first number: "))
num2 = int(input("Enter the second number: "))
result = num1 / num2
with open("sample1.txt", "r") as file:
content = file.read()
except Exception as e:
print(f"An error occurred: {e}")
Why is it a problem?
You got a user input and performed a division which is okay. Opening a file within the try
block is wrong. A better approach would be to remove the code opening a file.
Conclusion
We can draw the following conclusion from this lesson:
- Handling exceptions prevents your program from crashing unexpectedly.
- We can use multiple except blocks.
- The error message should be easily understood.
- Raise exceptions wisely.
If you enjoyed reading this, you just need to do this. Follow me on Twitter and LinkedIn.
Top comments (0)