Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 5

Error Handling with Try-Except

Recommended resources

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:

  1. try block: You put your "risky" code inside this block. Python will try to execute it.
  2. except block: If an exception occurs inside the try block, Python immediately stops executing the rest of the try block and jumps to the except block. 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.

  • else block: This code runs only if the try block completes successfully (i.e., no exceptions were raised). It's useful for separating your "success" code from the "risky" code in the try block.
  • finally block: This code always runs, no matter what. It runs if the try block 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.

Python try-except-else-finally Flowchart
This flowchart shows the execution path. Python runs the `try` block. If an error occurs, it jumps to the `except` block, then to `finally`. If no error occurs, it runs the `else` block, then `finally`.

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?

  1. The user enters 10 for the numerator and 2 for the denominator.
  2. The user enters 10 for the numerator and 0 for the denominator.
Show answer
  1. Scenario 1 (input: 10 and 2):

    • The try block succeeds.
    • The else block runs.
    • The finally block runs.
    • Output:
      The result is 5.0.
      Calculation attempt finished.
      
  2. Scenario 2 (input: 10 and 0):

    • A ZeroDivisionError occurs in the try block.
    • The except ZeroDivisionError block runs.
    • The finally block runs.
    • Output:
      You cannot divide by zero!
      Calculation attempt finished.
      

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.

  1. Create a new Python file.
  2. Define a function robust_word_counter(data).
  3. Inside the function, use a try-except block.
    • In the try block, attempt to split the data and 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 return 0.
  4. 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-except block 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 generic except block.
  • The else block runs only when the try block succeeds without any errors.
  • The finally block 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.