Sitemap
Python in Plain English

New Python content every day. Follow to join our 3.5M+ monthly readers.

Mastering Python Exceptions

Learn about Python’s built-in exceptions and error handling

11 min readJul 16, 2024

--

Press enter or click to view image in full size
Photo by Brett Jordan on Unsplash

Python, being one of the most popular programming languages, is known for its simplicity and readability. However, even the most seasoned developers encounter errors and unexpected situations.

This is where exceptions come in handy. Exceptions are special events that indicate something has gone wrong during the execution of a program. Rather than allowing the program to crash abruptly, exceptions provide a controlled way to manage these errors. Python offers a robust set of built-in exceptions designed to help developers identify and handle various error conditions gracefully.

By using these built-in exceptions, developers can write code that anticipates potential issues, responds appropriately, and maintains the program’s stability and reliability. Understanding and effectively utilizing exceptions is crucial for writing robust, error-resilient Python code.

Did you know that you can clap up to 50 times per article? Well now you do! Please consider helping me out by clapping and following me! 😊

Hi, my name is CyCoderX and in this article, we will explore the world of Python’s built-in exceptions. We will delve into their hierarchy, understand common exceptions, and learn how to handle them effectively in your code. Whether you are a beginner or an experienced developer, mastering exceptions is crucial for writing robust and error-free code.

Please note that I have a dedicated guide on Exception Handling in Python, that covers try and except blocks, which is useful to combine with this article.

Ready to dive into the world of Python exceptions? Let’s get started!

Interested in more SQL or Python content? Don’t worry I got you covered! Feel free to explore some of my lists on medium!

Understanding Exceptions in Python

In Python, exceptions are a way to signal that something unexpected has occurred. When an exception is raised, the normal flow of the program is interrupted, and the interpreter looks for a way to handle the error. If the exception is not handled, the program will terminate with a traceback, providing details about what went wrong.

What is an Exception?

An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions. It is a signal that something unexpected happened, such as an error in logic, invalid input, or a missing resource.

The try-except Block

Python uses the try-except block to handle exceptions. Here’s a basic example:

try:
# Code that may raise an exception
result = 10 / 0
except ZeroDivisionError:
# Code to execute if a ZeroDivisionError occurs
print("You can't divide by zero!")

In this example, dividing by zero raises a ZeroDivisionError. The except block catches the exception and provides a way to handle it, preventing the program from crashing.

Raising Exceptions

You can also raise exceptions intentionally using the raise keyword. This is useful when you want to signal that an error has occurred in your code:

def validate_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
return age

try:
validate_age(-1)
except ValueError as e:
print(f"Error: {e}")

In this example, the validate_age function raises a ValueError if the age is negative. The except block catches the exception and prints an error message.

Built-in Exceptions

Python comes with a wide range of built-in exceptions to handle various error situations. These exceptions are organized in a hierarchy, with BaseException at the top. All built-in exceptions are derived from BaseException, making it easy to catch all exceptions if needed.

Enjoying my article and want to read more about SQL and Databases ? Not to worry! I got you covered with dedicated lists and articles! Click here to check out my list on Medium.

Hierarchy of Built-in Exceptions

Understanding the hierarchy of built-in exceptions in Python is crucial for effective error handling. Python’s exceptions are organized in a tree structure, with BaseException at the root. This hierarchy allows developers to catch exceptions at various levels of specificity.

Here is a simplified view of the exception hierarchy:

BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception
├── StopIteration
├── ArithmeticError
│ ├── ZeroDivisionError
│ ├── OverflowError
│ └── FloatingPointError
├── AssertionError
├── AttributeError
├── BufferError
├── EOFError
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
├── NameError
│ └── UnboundLocalError
├── OSError
│ ├── BlockingIOError
│ ├── ChildProcessError
│ ├── ConnectionError
│ │ ├── BrokenPipeError
│ │ ├── ConnectionAbortedError
│ │ ├── ConnectionRefusedError
│ │ └── ConnectionResetError
│ ├── FileExistsError
│ ├── FileNotFoundError
│ ├── InterruptedError
│ ├── IsADirectoryError
│ ├── NotADirectoryError
│ ├── PermissionError
│ ├── ProcessLookupError
│ └── TimeoutError
├── ReferenceError
├── RuntimeError
│ ├── NotImplementedError
│ ├── RecursionError
├── SyntaxError
│ └── IndentationError
│ └── TabError
├── SystemError
├── TypeError
├── ValueError
│ └── UnicodeError
│ ├── UnicodeDecodeError
│ ├── UnicodeEncodeError
│ └── UnicodeTranslateError
└── Warning
├── DeprecationWarning
├── PendingDeprecationWarning
├── RuntimeWarning
├── SyntaxWarning
├── UserWarning
├── FutureWarning
└── ImportWarning
└── UnicodeWarning
└── BytesWarning
└── ResourceWarning

BaseException

BaseException is the base class for all exceptions in Python. It's rarely used directly but serves as a superclass for all other exceptions.

Exception

Exception is the most commonly used base class for most built-in exceptions. When you create custom exceptions, you typically inherit from Exception.

SystemExit, KeyboardInterrupt, and GeneratorExit

These exceptions are derived directly from BaseException and are used for controlling the interpreter. For instance:

  • SystemExit is raised when the sys.exit() function is called.

Understanding this hierarchy helps in writing more effective exception handling code. For instance, you can catch a broad exception (like ArithmeticError) to handle multiple related errors (such as ZeroDivisionError, OverflowError, and FloatingPointError) in a single except block.

Python Built-in Exceptions with Descriptions

Are you also interested in Python content? Click here to learn essential tips and tricks, along with best practices to master Python!

Common Built-in Exceptions and Their Uses

Python provides a wide range of built-in exceptions to handle various error conditions. Let’s explore some of the most commonly encountered exceptions and their uses.

SyntaxError

A SyntaxError occurs when the parser encounters a syntax error. This happens when the code is not valid Python.

Example:

# SyntaxError: missing colon
def function()
print("Hello, World!")

TypeError

A TypeError occurs when an operation or function is applied to an object of inappropriate type.

Example:

# TypeError: can't multiply sequence by non-int of type 'float'
result = 'Hello' * 2.5

ValueError

A ValueError occurs when a function receives an argument of the right type but inappropriate value.

Example:

# ValueError: invalid literal for int() with base 10: 'abc'
number = int('abc')

IndexError

An IndexError occurs when trying to access an element from a list using an index that is out of range.

Example:

my_list = [1, 2, 3]
# IndexError: list index out of range
print(my_list[5])

KeyError

A KeyError occurs when trying to access a dictionary with a key that does not exist.

Example:

my_dict = {'name': 'Alice', 'age': 30}
# KeyError: 'address'
print(my_dict['address'])

AttributeError

An AttributeError occurs when an invalid attribute reference or assignment is attempted.

Example:

class MyClass:
pass

obj = MyClass()
# AttributeError: 'MyClass' object has no attribute 'attribute'
print(obj.attribute)

ImportError

An ImportError occurs when an import statement fails to find the module definition or when a from ... import statement fails to find a name that is to be imported.

Example:

# ImportError: No module named 'nonexistent_module'
import nonexistent_module

Handling Multiple Exceptions

You can handle multiple exceptions in a single try-except block by specifying multiple exception types in a tuple.

Example:

try:
value = int('abc')
except (ValueError, TypeError) as e:
print(f"Caught an exception: {e}")

In this example, the code will catch both ValueError and TypeError exceptions.

Get CyCoderX’s stories in your inbox

Join Medium for free to get updates from this writer.

Understanding these common exceptions and their use cases will help you write better error-handling code.

Handling Exceptions with try-except

In Python, handling exceptions gracefully is crucial for building robust applications. The try-except block is the primary mechanism for catching and handling exceptions.

Please note that I have a dedicated guide on Exception Handling in Python, that covers try and except blocks, which is useful to combine with this article.

Let's explore how to use it effectively.

Basic try-except Block

The simplest form of exception handling uses a try block followed by an except block. Here's an example:

try:
# Code that may raise an exception
result = 10 / 0
except ZeroDivisionError:
# Code to execute if a ZeroDivisionError occurs
print("You can't divide by zero!")

In this example, the ZeroDivisionError is caught, and a user-friendly message is displayed instead of terminating the program.

Handling Multiple Exceptions

You can handle multiple exceptions by specifying them in a tuple within the except clause:

try:
value = int('abc')
except (ValueError, TypeError) as e:
print(f"Caught an exception: {e}")

This code catches both ValueError and TypeError, providing a single block to handle either exception.

Using Else and Finally

You can use else and finally clauses to add more control to your exception handling:

  • else is executed if no exceptions are raised.

Example:

try:
result = 10 / 2
except ZeroDivisionError:
print("You can't divide by zero!")
else:
print(f"The result is {result}")
finally:
print("Execution completed.")

In this example, the else block runs if no exceptions occur, and the finally block runs regardless of the outcome.

Catching All Exceptions

While not always recommended, you can catch all exceptions using the base Exception class. This can be useful for logging errors or providing a generic error message.

Example:

try:
result = 10 / 'a'
except Exception as e:
print(f"An error occurred: {e}")

This code catches any exception that may occur and prints a generic error message.

Raising Exceptions

You can raise exceptions intentionally using the raise keyword. This is useful for signaling specific error conditions in your code.

Example:

def validate_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
return age

try:
validate_age(-1)
except ValueError as e:
print(f"Error: {e}")

In this example, the validate_age function raises a ValueError if the age is negative, and the except block catches and handles it.

Nested try-except Blocks

You can nest try-except blocks to handle exceptions at different levels of your code:

try:
try:
result = 10 / 0
except ZeroDivisionError:
print("Inner catch: You can't divide by zero!")
raise
except Exception as e:
print(f"Outer catch: An error occurred: {e}")

In this example, the inner try-except block handles the ZeroDivisionError, re-raises it, and the outer block catches it.

Handling exceptions properly ensures your programs can manage unexpected situations gracefully, enhancing their robustness and reliability.

Customizing Exception Handling

In addition to using Python’s built-in exceptions, you can create custom exceptions to handle specific error conditions in your applications. Custom exceptions make your code more readable and maintainable by providing clear, specific error messages.

Creating Custom Exceptions

To create a custom exception, define a new class that inherits from Python’s built-in Exception class (or any of its subclasses). Here's a basic example:

class NegativeAgeError(Exception):
"""Exception raised for errors in the input age."""

def __init__(self, age, message="Age cannot be negative"):
self.age = age
self.message = message
super().__init__(self.message)

# Using the custom exception
def validate_age(age):
if age < 0:
raise NegativeAgeError(age)
return age

try:
validate_age(-1)
except NegativeAgeError as e:
print(f"Error: {e}")

In this example, the NegativeAgeError class is defined to handle cases where a negative age is provided. The custom exception includes the invalid age and a default error message.

Adding Attributes and Methods

Custom exceptions can have additional attributes and methods to provide more context or functionality:

class NegativeAgeError(Exception):
"""Exception raised for errors in the input age."""

def __init__(self, age, message="Age cannot be negative"):
self.age = age
self.message = message
super().__init__(self.message)

def __str__(self):
return f'{self.message}: {self.age}'

def validate_age(age):
if age < 0:
raise NegativeAgeError(age)
return age

try:
validate_age(-5)
except NegativeAgeError as e:
print(f"Error: {e}")

In this enhanced version, the __str__ method is overridden to provide a custom string representation of the exception, which includes the invalid age.

Using Multiple Custom Exceptions

You can define multiple custom exceptions to handle different error conditions:

class NegativeAgeError(Exception):
"""Exception raised for errors in the input age."""
pass

class AgeTooHighError(Exception):
"""Exception raised for errors in the input age being too high."""
pass

def validate_age(age):
if age < 0:
raise NegativeAgeError("Age cannot be negative")
elif age > 150:
raise AgeTooHighError("Age cannot be greater than 150")
return age

try:
validate_age(200)
except NegativeAgeError as e:
print(f"Error: {e}")
except AgeTooHighError as e:
print(f"Error: {e}")

In this example, two custom exceptions, NegativeAgeError and AgeTooHighError, are used to handle different error scenarios related to age validation.

Best Practices for Custom Exceptions

  1. Inherit from Exception or its Subclasses: Always inherit from Exception or a more specific built-in exception class.

Custom exceptions are powerful tools for handling application-specific errors, making your code cleaner and more maintainable.

Conclusion

Handling exceptions is an essential skill for any Python developer. By understanding and using Python’s built-in exceptions, and by creating custom exceptions, you can write more robust and reliable code. Whether you are dealing with common errors like TypeError and ValueError, or defining your own error conditions, proper exception handling ensures that your programs can gracefully manage unexpected situations.

Mastering exceptions not only helps you debug and maintain your code but also enhances the user experience by providing meaningful error messages and preventing crashes.

Happy coding!

Press enter or click to view image in full size
Photo by Mark Fuller on Unsplash

Final Words:

Thank you for taking the time to read my article.

This article was first published on medium by CyCoderX.

Hey There! I’m CyCoderX, a data engineer who loves crafting end-to-end solutions. I write articles about Python, SQL, AI, Data Engineering, lifestyle and more!

Join me as we explore the exciting world of tech, data and beyond!

For similar articles and updates, feel free to explore my Medium profile

If you enjoyed this article, consider following for future updates.

Interested in Python content and tips? Click here to check out my list on Medium.

Interested in more SQL, Databases and Data Engineering content? Click here to find out more!

Happy Coding!

What did you think about this article? Let me know in the comments below … or above, depending on your device 🙃

In Plain English 🚀

Thank you for being a part of the In Plain English community! Before you go:

--

--

CyCoderX
CyCoderX

Written by CyCoderX

Machine Learning & Data Engineer | Data Science | Python & SQL | AI | Software Developer | Azure & AWS Cloud Specialist | Blogger simplifying Big Data & Trends

Python in Plain English
Python in Plain English

Published in Python in Plain English

New Python content every day. Follow to join our 3.5M+ monthly readers.