Hello! Welcome to the final lesson in our Python fundamentals module.
In our last session, you learned the crucial skill of file handling, which allows your game to save and load progress. You even got a small preview of today's topic when we used a try-except block to handle a FileNotFoundError. This prevented the program from crashing if a save file didn't exist.
Just as a musician learns to recover smoothly from a missed note during a performance, a programmer must teach their program how to recover from unexpected situations. If your game crashes every time the player does something unforeseen, they won't have a very good experience.
Today, we'll master this skill. Your learning outcome is to handle common errors using try-except blocks. This will make your code more robust and user-friendly, an essential step as we prepare to build a real game.
Why Do We Need Error Handling?
When your Python code encounters a situation it can't resolve, it raises an exception and, if unhandled, crashes. This prints a traceback to the console, which is helpful for you as a developer but confusing and jarring for a player.
Let's see what this looks like and how to prevent it.
Python Error Handling - Visually Explained
The video 'Python Error Handling - Visually Explained' does a fantastic job of showing why unhandled exceptions are a problem and introduces the basic try-except structure as the solution.
Watch from the beginning to 02:52. Pay attention to how the program crashes without error handling and how a simple try-except block can provide a much better user experience.
As the video shows, wrapping potentially problematic code in a try block gives you a way to catch exceptions and handle them gracefully in an except block.
try:
# Code that might cause an error
age_str = input("Enter your character's age: ")
age = int(age_str)
print(f"Your character is {age} years old.")
except:
# Code that runs ONLY if an error occurred in the try block
print("That wasn't a valid number. Please try again.")
print("Program continues...")
If you run this code and enter "twenty" instead of "20", the int() conversion will fail. Instead of crashing, the program will execute the except block and then continue running.
Being Specific: Handling Different Types of Errors
The except block in the example above is a "catch-all." It will catch any error, which can sometimes hide bugs. A better practice is to handle specific, anticipated exceptions.
For example, our calculator from the video could fail in two ways:
- The user enters text instead of a number (
ValueError). - The user enters 0 for the denominator (
ZeroDivisionError).
We can use multiple except blocks to handle each case with a specific message.
Python Error Handling - Visually Explained
Let's continue with 'Python Error Handling - Visually Explained' to see how to handle multiple, specific exceptions.
Watch from 02:52 to 04:44. Notice how the code uses except ValueError: and except ZeroDivisionError: to provide more relevant feedback to the user.
Here is how you would structure code to handle specific errors:
try:
numerator = int(input("Enter numerator: "))
denominator = int(input("Enter denominator: "))
result = numerator / denominator
print(f"Result is {result}")
except ValueError:
print("Error: Please enter valid numbers.")
except ZeroDivisionError:
print("Error: Cannot divide by zero.")
It's also possible to access the error message itself using the as keyword.
Python Tutorial: Using Try/Except Blocks for Error Handling
For a more in-depth look at catching specific exceptions and accessing the error object, this video from Corey Schafer is an excellent resource.
Watch from the beginning to 05:39. This clip explains the 'try-except' flow, the importance of catching specific exceptions over general ones, and how to use as e to print the actual error message.
Test your understanding!
In our RPG, an inventory is a list of items. What happens if you try to access inventory[5] but the player only has 3 items? You'll get an IndexError.
Write a function get_item_from_inventory(inventory, index) that attempts to return an item from the inventory at a given index. The function should:
- Return the item if the index is valid.
- Catch an
IndexErrorand return the string"No item at that slot."if the index is out of bounds.
player_inventory = ["Sword", "Shield", "Potion"]
# Test your function with a valid index and an invalid one.
# For example: get_item_from_inventory(player_inventory, 1)
# And: get_item_from_inventory(player_inventory, 5)
Show answer
def get_item_from_inventory(inventory, index):
try:
return inventory[index]
except IndexError:
return "No item at that slot."
player_inventory = ["Sword", "Shield", "Potion"]
# Test cases
print(f"Item at index 1: {get_item_from_inventory(player_inventory, 1)}")
# Expected output: Item at index 1: Shield
print(f"Item at index 5: {get_item_from_inventory(player_inventory, 5)}")
# Expected output: Item at index 5: No item at that slot.
The Full Structure: else and finally
The try-except statement has two more optional clauses: else and finally. They give you even finer control over your program's flow.
else: This block runs only if thetryblock completes successfully (i.e., no exceptions were raised). It's useful for code that should only run on the "happy path."finally: This block runs no matter what. It executes whether thetryblock succeeded or failed. It's perfect for cleanup actions, like closing a file or a network connection.
This flowchart provides a great visual summary of how these blocks work together.

Let's watch a video that explains these clauses in detail.
Python Tutorial: Using Try/Except Blocks for Error Handling
Corey Schafer's tutorial clearly explains the purpose of the else and finally clauses.
Watch from 05:39 to 08:35. Focus on when the else block is executed versus when the finally block is executed.
Here's an example that combines everything, using our file-handling knowledge from the last lesson:
try:
f = open('game_config.txt', 'r')
# Let's pretend we read and parse the file here
except FileNotFoundError:
print("Config file not found. Using default settings.")
else:
# This runs only if the file was successfully opened
print("Config file loaded successfully.")
# We can close the file here, because we know 'f' exists
f.close()
finally:
# This runs regardless of what happened
print("Configuration setup finished.")
Note: In the previous lesson, we learned to use with open(...), which automatically handles closing the file for you. This is generally preferred over manually calling f.close() in an else or finally block.
Creating Your Own Errors with raise
Sometimes, you need to create an error yourself. For instance, your game's logic might dictate that a player can't use a certain item outside of combat. This isn't a built-in Python error, but you can signal it as one using the raise keyword.
Python Tutorial: Using Try/Except Blocks for Error Handling
The final part of Corey Schafer's video demonstrates how to manually raise your own exceptions.
Watch from 08:35 to 10:04. Understand that raise lets you create an exception at any point in your code, which can then be caught by an except block.
This is very powerful for enforcing game rules.
def use_special_attack(player_in_combat):
if not player_in_combat:
raise ValueError("Special attacks can only be used in combat!")
else:
print("Player uses Whirlwind Strike!")
try:
# Player is just walking around the map
is_in_combat = False
use_special_attack(is_in_combat)
except ValueError as e:
# The 'e' variable contains the error message we wrote
print(f"Cannot perform action: {e}")
Practice Time
To solidify your understanding, it's best to write some code. The following resource provides a great set of exercises.
Python Exception Handling: Exercises, Solutions, and Practice
The 'Python Exception Handling: Exercises' page on w3resource is a great place to practice. I recommend trying a few specific problems that are highly relevant to game development.
Review the list of exercises. Try to solve problems #2 (ValueError), #3 (FileNotFoundError), and #6 (IndexError) on your own. You can click the link below each problem to see the sample solution if you get stuck.
Conclusion
Congratulations on completing the first module of this course! You have built a strong foundation in Python, from the basics of variables and loops to the complexities of classes and now, robust error handling.
Here are the key takeaways from today's lesson:
- Wrap code that might fail in a
tryblock to prevent your program from crashing. - Use
exceptblocks to handle errors. It's best practice to catch specific exceptions (ValueError,FileNotFoundError, etc.) rather than a generic one. - The
elseblock runs only if thetryblock succeeds, making it ideal for "happy path" logic. - The
finallyblock runs no matter what, making it perfect for essential cleanup tasks. - Use the
raisekeyword to create your own errors and enforce rules in your program's logic.
In our next lesson, we will begin the most exciting part of our journey yet. We will move into Module 2: Introduction to Game Development with Pygame. You'll set up your game development environment and, for the first time, create a visual game window on the screen. The Python skills you've just solidified are the bedrock upon which you'll build your RPG.