Hello! Welcome to your fifth lesson in the Python Fundamentals module.
In our last lesson, you learned how to write functions with parameters and return values. This was a huge step in making your code organized and reusable, as we saw when we created the count_words function.
But what happens when things don't go as planned? For example, what if a user tries to divide a number by zero in a calculator program? Or what if your text analysis app receives an empty text box to analyze? Right now, our scripts would simply crash and show an ugly error message. This isn't ideal for a real application.
Today, we will learn how to handle these situations gracefully. The learning outcome for this lesson is to handle potential errors in a script using try-except blocks. This skill is essential for building robust programs that don't break easily, which will be critical for the web application you aim to build.
What is an Exception?
In programming, when an unexpected error occurs that disrupts the normal flow of your script, it's called an exception. Python "raises" an exception when it encounters a situation it can't handle. If this exception isn't "caught," your program stops.
Let's look at some code that would cause exceptions:
# This will cause a ValueError because 'hello' cannot be converted to an integer.
# number = int("hello")
# This will cause a ZeroDivisionError.
# result = 10 / 0
# This will cause a TypeError because you can't add a string and an integer.
# 'hello' + 5
Running any of these lines would crash your program. To see this in action and learn about the most common types of exceptions, watch the beginning of this quick video.
Learn Python EXCEPTION HANDLING in 5 minutes! 🚦
This video from Bro Code gives a fast and clear introduction to what exceptions are and shows a few common types you'll encounter.
Watch the first minute of the video (00:00 - 01:02). Focus on understanding that an exception is an event that interrupts your program, and notice the names of the errors like ZeroDivisionError and ValueError.
The try and except Blocks: Your Safety Net
Instead of letting these exceptions crash our program, we can anticipate and handle them using a try-except block. The structure is simple:
tryblock: You put your "risky" code inside this block. Python will try to execute it.exceptblock: If an exception occurs inside thetryblock, Python immediately stops executing the rest of thetryblock and jumps to theexceptblock. The code here defines how you want to handle the error.
Let's fix our number-conversion example:
try:
# This is the risky operation
number = int(input("Enter a number: "))
print(f"You entered the number {number}.")
except:
# This code runs ONLY if an error occurred above
print("That was not a valid number!")
print("The program continues on...")
If you run this code and enter "hello", the program won't crash. It will print "That was not a valid number!" and then "The program continues on...". You've successfully handled the error!
To get a deeper understanding of this flow, let's watch another great explanation.
Python Tutorial: Using Try/Except Blocks for Error Handling
This video from Corey Schafer provides a clear, step-by-step walkthrough of why we need try-except blocks and how they work.
Watch from the beginning to 02:07. The example uses file handling, but the core concept is the same. Focus on how the try block isolates the dangerous code and the except block provides a user-friendly response instead of a crash.
Being Specific: Handling Different Types of Errors
In the example above, we used a general except: block. This catches any and all exceptions. While it seems convenient, it's often considered bad practice because it can hide bugs you didn't expect.
It's much better to be specific about the exceptions you want to handle. This way, you handle the errors you anticipate (like a user typing text instead of a number) but let unexpected errors crash the program so you know you have a bug to fix.
You can have multiple except blocks to handle different types of errors.
try:
numerator = int(input("Enter a number to divide: "))
denominator = int(input("Enter a number to divide by: "))
result = numerator / denominator
print(f"The result is {result}.")
except ValueError:
print("Invalid input! Please enter only numbers.")
except ZeroDivisionError:
print("You cannot divide by zero!")
Now, our code gives a specific, helpful message for each of the two errors we expect might happen.
Beginners Guide To Try-Except In Python
To reinforce this idea, let's read a short guide. It provides a clear step-by-step process for building a robust try-except structure.
Read the section titled 'How to use try-except in Python', focusing on steps 1, 2, and 3 ('Start with a try block', 'Add an except block', and 'How to handle multiple different errors').
Adding else and finally for More Control
The try-except structure can be extended with two more optional clauses: else and finally. They give you even finer control over your program's flow.
elseblock: This code runs only if thetryblock completes successfully (i.e., no exceptions were raised). It's useful for separating your "success" code from the "risky" code in thetryblock.finallyblock: This code always runs, no matter what. It runs if thetryblock succeeds, and it runs if an exception occurs. It's perfect for cleanup actions, like closing a file or a network connection, ensuring it happens regardless of errors.
This flowchart provides an excellent visual summary of how all four parts work together.

Here's our calculator example, now with else and finally:
try:
numerator = int(input("Enter a number to divide: "))
denominator = int(input("Enter a number to divide by: "))
result = numerator / denominator
except ValueError:
print("Invalid input! Please enter only numbers.")
except ZeroDivisionError:
print("You cannot divide by zero!")
else:
# This runs only if the division was successful
print(f"The result is {result}.")
finally:
# This runs no matter what
print("Calculation attempt finished.")
Test your understanding!
Consider the code snippet above. What will be printed in the following two scenarios?
- The user enters
10for the numerator and2for the denominator. - The user enters
10for the numerator and0for the denominator.
Show answer
-
Scenario 1 (input: 10 and 2):
- The
tryblock succeeds. - The
elseblock runs. - The
finallyblock runs. - Output:
The result is 5.0. Calculation attempt finished.
- The
-
Scenario 2 (input: 10 and 0):
- A
ZeroDivisionErroroccurs in thetryblock. - The
except ZeroDivisionErrorblock runs. - The
finallyblock runs. - Output:
You cannot divide by zero! Calculation attempt finished.
- A
Try It Yourself: Make a Robust Function
Let's make the count_words function from our last lesson more robust. The original function assumes it will always receive a string. If it receives another data type, like an integer, it will crash with an AttributeError because integers don't have a .split() method.
Your task is to rewrite the function to handle this.
- Create a new Python file.
- Define a function
robust_word_counter(data). - Inside the function, use a
try-exceptblock.- In the
tryblock, attempt to split thedataand return its length. - In the
except AttributeError:block, handle the case where the data is not a string. Print a message like "Error: Input must be a string." and return0.
- In the
- Test your function by calling it with both a valid string and an integer to see the error handling in action.
Solution
def robust_word_counter(data):
"""
Takes any data, tries to count words if it's a string,
and handles errors gracefully if it's not.
"""
try:
words = data.split()
count = len(words)
return count
except AttributeError:
# This error happens if data doesn't have a .split() method (e.g., an integer)
print("Error: Input must be a string.")
return 0
# --- Let's test it ---
sentence = "This is a valid sentence."
not_a_sentence = 12345
count1 = robust_word_counter(sentence)
print(f"The word count is: {count1}")
print("-" * 20)
count2 = robust_word_counter(not_a_sentence)
print(f"The word count is: {count2}")
# Expected Output:
# The word count is: 5
# --------------------
# Error: Input must be a string.
# The word count is: 0
Key Takeaways
Great job! You've learned how to make your programs much more reliable. This is a skill that separates beginner code from professional code.
- An exception is an error that occurs during execution and can crash your program.
- A
try-exceptblock allows you to "catch" exceptions and handle them gracefully instead of crashing. - It's best practice to catch specific exceptions (
ValueError,TypeError, etc.) rather than using a genericexceptblock. - The
elseblock runs only when thetryblock succeeds without any errors. - The
finallyblock runs no matter what, making it perfect for cleanup tasks.
Next Steps
This lesson completes our "Python Fundamentals" module. You now have the foundational skills to write scripts, organize data, create reusable functions, and handle errors.
The next step is to prepare our environment for building a web application. In the next lesson, we will learn how to install and manage external Python packages using pip. pip is Python's package installer, and it lets you easily add powerful, pre-written code to your projects. This will be essential for installing Flask (our web framework) and the Google Gemini library later in the course.