Introduction
Exception handling is a mechanism to manage errors that arise during program execution, commonly referred to as runtime errors. Such errors, for example, can be file I/O operations, network requests, or mathematical calculations. In Python, try
–except
statements are employed to specify where you anticipate the errors to occur and how to handle them.
The exception itself is a type of Python object that is “raised” to alert different parts of the program that an error occurred. Exception can either be raised by the Python interpreter behind the scenes, or by your own code using the raise
keyword.
Basics of try–except Statement Syntax
Let’s ease into exception handling concepts by looking at the very basic try
–except
statement syntax. This syntax consists of the try
keyword followed by a colon :
with a code block underneath. After that, the except
keyword specifies the type of exception to handle, and is again followed by a colon :
and another code block.
try: try-block except ExceptionType: except-block
try
: The keyword that starts the statement.try-block
: The block of code where you anticipate exceptions. If an exception occurs within this block, Python looks for an appropriateexcept
block to handle it.except
: Defines the exception handling. If an exception of typeExceptionType
or any of its subclasses occurs within thetry
block, no further code is executed intry-block
and the code withinexcept-block
is run next.except-block
: The code that handles the exception.
The process of intercepting an exception for custom handling is also referred to as “catching” an exception.
Example:
try: numerator = int(input("Enter the numerator")) denominator = int(input("Enter the denominator")) fraction = numerator/denominator print(f"The decimal fraction is {fraction}.") except ZeroDivisionError: print("The fraction is invalid because the denominator is zero.")
Output 1:
Enter the numerator> 2 Enter the denominator> 5 The decimal fraction is 0.4.
Output 2:
Enter the numerator>? 2 Enter the denominator>? 0 The fraction is invalid because the denominator is zero.
The example above asks the user to enter a numerator and a denominator and then prints a decimal fraction by dividing them. Line 1 starts the try
–except
statement, with the try
block indented underneath. The except
on line 7 handles any ZeroDivisionError
exceptions that happen in the try
block.
The first sample run shows the program running normally with no exception. The user inputs 2 and 5, and the fraction 0.4 is printed by line 6 of the code.
The second sample output demonstrates the ZeroDivisionError
exception being handled. Here, the user enters 2 and 0, causing the division on line 5 to fail and raise a ZeroDivisionError
exception. When this happens, no further code is executed in the try
block (the print is skipped) and the code goes to line 8 and prints the appropriate message.
Now, with the same example program, let’s see what happens when an exception that is not ZeroDivisionError
happens. We’ll keep the code the same, but we’ll enter a string that cannot be converted into a number.
Output:
Enter the numerator> Hello Traceback (most recent call last): File "Contents/plugins/python-ce/helpers/pydev/pydevconsole.py", line 364, in runcode coro = func() File "<input>", line 2, in <module> ValueError: invalid literal for int() with base 10: 'Hello'
This second run of the program receives the string “Hello” at the numerator prompt. Obviously, it cannot be converted into a number, and therefore, line 2 fails with a ValueError
exception. But this type of exception is not specified by the except
, and therefore, its block does not handle it, and the nice message is not printed. Instead, the exception is propagated to the calling code, which in this case is the terminal. The terminal handles it by printing the exception information, starting on line 3.
Handling Multiple Exceptions
The previous example handled a ZeroDivisionError
exception but let a ValueError
go through. Let’s see how we can handle a ValueError
as well.
Example:
try: numerator = int(input("Enter the numerator")) denominator = int(input("Enter the denominator")) fraction = numerator/denominator print(f"The decimal fraction is {fraction}.") except (ZeroDivisionError, ValueError): print("The value you entered is invalid.")
Output:
Enter the numerator> 2 Enter the denominator> 0 The value you entered is invalid.
Output:
Enter the numerator>? Hello again The value you entered is invalid.
This time, the example above handles both a ZeroDivisionError
and a ValueError
. It accomplishes this by listing all the exceptions to handle within a comma-separated list, enclosed by parentheses ()
on line 8. The block underneath the except
will be executed when any one of the listed exceptions occurs.
The previous example handled both the ZeroDivisionError
and ValueError
exceptions with the same code block, which printed the same generic message. However, these are generated by two different types of user input problems, and that’s why there is a way to handle each exception separately. The next example will do exactly that to let the user know what went wrong more specifically.
Example:
try: numerator = int(input("Enter the numerator")) denominator = int(input("Enter the denominator")) fraction = numerator/denominator print(f"The decimal fraction is {fraction}.") except ZeroDivisionError: print("The fraction is invalid because the denominator is zero.") except ValueError: print("The input you entered is invalid because it can't be converted to a number.")
Output 1:
Enter the numerator> 2 Enter the denominator> 0 The fraction is invalid because the denominator is zero.
Output 2:
Enter the numerator>? Hello again The input you entered is invalid because it can't be converted to a number.
In the example above, each of the errors is handled differently by two separate except
clauses. The except
on line 7 handles the ZeroDivisionError
, and the one on line 9, ValueError
. Each prints a different message when either the denominator is zero, or the input can’t be converted to a number, respectively.
The general syntax for the try
–except
for handling multiple exceptions in the try
block is as follows:
try: try-block except ExceptionType1: except-block1 except (ExceptionType2, ExceptionType3): except-block2
Handling All Exceptions
If you omit the exception type in the except
clause, you can catch and handle all exceptions.
The basic syntax for this is as follows:
try: try-block except: except-block
While this can be useful for generic error handling, it’s generally good practice to catch specific exceptions whenever possible. A catch-all exception handler will not only handle anticipated exceptions, but also hide unanticipated exceptions you may want to be aware of and handle differently.
Example:
try: numerator = int(input("Enter the numerator")) denominator = int(input("Enter the denominator")) fraction = numerator / denominator print(f"The decimal fraction is {fraction}.") except: print("Something bad happened.")
Output:
Enter the numerator> 2 Enter the denominator> 0 Something bad happened.
The example above catches all exceptions on line 6 and handles them in the block below on line 7.
Another way to catch all exceptions is to catch the Exception
type. Doing this will catch all because Exception
is the base class of all Python exceptions, and the except
clause catches all the exceptions it’s given and their descendant types.
Example:
try: numerator = int(input("Enter the numerator")) denominator = int(input("Enter the denominator")) fraction = numerator / denominator print(f"The decimal fraction is {fraction}.") except Exception: print("Something bad happened again.")
Output:
Enter the numerator> 2 Enter the denominator> 0 Something bad happened again.
This example is very similar to the previous one, however on line 6, the except
clause is given the Exception
type, which is another way of catching all exceptions.
Accessing Exception Information
As objects, Python exceptions have some properties that can be accessed within the except
block. To do so, you use the as
keyword followed by a variable name after the exception type in the except
clause. The exception object will then become accessible in the except
block through a variable of that name.
Here is the basic syntax for using as
:
try: try-block except ExceptionType as e: except-block
Inside except-block
, the properties of the exception e
can be accessed like they can for other Python objects.
Example:
try: x = 1/0 except ZeroDivisionError as e: print(f"An exception of type {type(e).__name__} occurred: {e}")
Output:
An exception of type ZeroDivisionError occurred: division by zero
In the example above, the exception variable e
is designated on line 3. It is assigned the exception object that occurs due to the division by zero on line 2. Inside the except
block, line 4 prints both the exception’s class name, i.e. ZeroDivisionError, and a description of the error. The class name is obtained as a string by accessing the class, using the type()
function, and then its __name__
property. The exception’s description, i.e. “division by zero”, is obtained directly from the exception object e
. Since e
is used within an f-string, Python implicitly converts it to a string using its __str__()
method. Writing e.__str__()
rather than just e
would achieve the same result here, but is unnecessary since Python does it automatically.
You can use as
with except
clauses that catch multiple exception types precisely the same way. The exception variable will assume whichever exception happens to occur.
Example:
try: numerator = int(input("Enter the numerator")) denominator = int(input("Enter the denominator")) fraction = numerator/denominator print(f"The decimal fraction is {fraction}.") except (ZeroDivisionError, ValueError) as e: print(f"An exception of type {type(e).__name__} occurred: {e.__str__()}")
Output 1:
Enter the numerator> I'm not a number An exception of type ValueError occurred: invalid literal for int() with base 10: "I'm not a number"
Output 2:
Enter the numerator> 2 Enter the denominator> 0 An exception of type ZeroDivisionError occurred: division by zero
The example above is again a variation of the now-very-familiar fraction program. The first sample run generates a ValueError exception, and the second run a ZeroDivisionError. The except
block prints the exception information on line 8, each time for the exception that occurred. You can see from the first output that the exception description is tailored to the individual error occurrence. The description includes the specific value it received for its erroneous input, “I’m not a number”.
Summary & Reference for the Python try–except Statements
Exception handling is a mechanism used to manage runtime errors that arise during program execution. In Python, try
–except
statements are used to specify where you anticipate the errors to occur and how to handle them.
The exception itself is a type of Python object that is “raised” to alert different parts of the program that an error occurred.
Python’s try
–except
statements are employed to specify the code block in which you anticipate exceptions to occur, and the code block with which you handle them.
try: try-block # Exception can occur here except ExceptionType: except-block # Handles exception of type `ExceptionType` or its descendant exception objects
Multiple exceptions can be handled using a single except
block by listing the exception types within parentheses and separating them with commas. Additionally, separate except
blocks can be used to handle different types of exceptions individually, allowing for customized exception handling logic for each exception.
try: try-block except ExceptionType1: except-block1 # Handles ExceptionType1 except (ExceptionType2, ExceptionType3): except-block2 # Handles ExceptionType2 and ExceptionType3
All exceptions can be caught and handled uniformly with a catch-all except
that has no specified exception type. It’s generally recommended to specify exception types whenever possible to maintain code clarity and avoid masking unexpected errors.
try: try-block except: except-block
Another way to catch all exceptions is to catch Exception
type, which is the base class for Python exceptions.
try: try-block except Exception: except-block
You can use the as
keyword within the except
clause to access detailed information about the raised exception, including its type and any associated error messages or data.
try: x = 1/0 except ZeroDivisionError as e: print(f"An exception of type {type(e).__name__} occurred: {e}")