Python handles exceptions using the try/except statement. Its general form looks like this:
| | try: |
| | try_block |
| | except optional_error_list: |
| | except_block |
| | else: |
| | optional_else_block |
| | finally: |
| | optional_finally_block |
The try block is a sequence of arbitrary statements (and may even include other exception-handling statements). As a pessimist, you assume that something inside this block or something it calls might raise an exception. Instead of executing the block blindly, you “try” to execute it.
If the try block runs successfully, control passes to the optional_else block (if present), or skips to after the try statement (otherwise).
If it fails, control passes to the except block. This block is not optional, even if it’s empty. In that case, it may contain only a no-op pass statement, which does nothing and serves as a placeholder, much like the _ variable described in the .
In the following example, we tried to print a nonexistent variable named ghosts and got an error:
| | >>> print(ghosts, end='') |
| | Traceback (most recent call last): |
| | File "<python-input-0>", line 1, in <module> |
| | print(ghosts, end='') |
| | ^^^^^^ |
| | NameError: name 'ghosts' is not defined |
We then wrapped the statement in a try/except block, raising the error from a syntax problem to a philosophical one:
| | >> try: |
| | ... print(ghosts, end='') |
| | ... except: |
| | ... print("Ghosts aren't real") # This! |
| | ... else: |
| | ... print(' and goblins') |
| | ... |
| | Ghosts aren't real |
Encouraged, we defined the missing variable and successfully produced the first line of a Halloween sing-along song for children:
| | >>> ghosts = 'Ghosts' |
| | >>> try: |
| | ... print(ghosts, end='') |
| | ... except: |
| | ... print("Ghosts aren't real") |
| | ... else: |
| | ... print(' and goblins') # This! |
| | ... |
| | Ghosts and goblins |
Trying to access an undefined variable is uncommon in Python. A more realistic example involves trying to read from a file that does not exist. Suppose data.txt is missing:
| | >>> try: |
| | ... with open('data.txt') as infile: |
| | ... data = infile.read() |
| | ... except: |
| | ... print("Failed to read data from 'data.txt'") |
| | ... |
| | Failed to read data from 'data.txt' |
The example above illustrates the power of the pessimistic approach. You could try to check whether the file exists, whether it’s readable, and whether the call to read succeeds, but none of that really matters. What you ultimately care about is whether the variable data was successfully initialized. The try/except construct answers exactly that question, letting your code stay focused on what matters.
As a rule of thumb, you should wrap code in a try/except block when you expect it might fail and want to separate problem-solving from error handling. Because error handling is specific to the code inside try, it is important to keep the try block compact; ideally, it should raise only one type of exception (for example, just file-related errors). A common mistake is to wrap your entire program in a single try/except. This moves all error handling, even syntax error handling, into the except block, and if it is not present, suppresses all errors by sweeping them under the rug until you realize your program simply doesn’t work.
Keep Your Exception Handlers Specific | |
|---|---|
| | Do not wrap your entire program in a single try/except block! Doing so will hide all errors, even syntax errors, and make a broken program appear to work correctly. |
An even better approach is to avoid generic, “catch-it-all” exception handlers and associate different handlers with different types of exceptions by specifying the optional_error_list, a comma-separated list of exception types. Inheritance () plays a factor here. For example, ArithmeticError exception handler will catch ZeroDivisionError because a ZeroDivisionError is a subclass of ArithmeticError and every division by zero is also an arithmetic error.
In the following example, the try/except statement has three clauses, each monitoring the progress of the operation and responding to a different condition. Here, we are interested only in the 11th character of the file, illustrating handling an IndexError (this and the following examples intentionally do not use a with block):
| | >>> try: |
| | ... infile = open('data.txt') |
| | ... data = infile.read()[10] |
| | ... except FileNotFoundError: |
| | ... print('File data.txt does not exist') |
| | ... except PermissionError: |
| | ... print('File data.txt is not readable') |
| | ... except IndexError: |
| | ... print('File data.txt is too short') |
| | ... |
| | File data.txt is too short |
You’ll see more built-in exception types in .
To construct a multiclause handler, you need to examine your code carefully, identify every possible exceptional condition, and decide if and how you want to handle it. The task can feel daunting, but the payoff is worth it: a robust, fault-tolerant program that can handle unexpected situations gracefully instead of crashing. Remember that at most one except clause is invoked per execution of the try block. The first matching handler would be called and the rest would be skipped.
The last optional block, optional_finally, if present, is executed after all other blocks, regardless of whether the operation succeeded or failed, even if one of the preceding blocks contains a return or raise (explained later in ) statement. You may use finally to clean up resources acquired in the try block—for example, to close files or network connections, as shown below:
| | >>> infile = None |
| | >>> try: |
| | ... infile = open('data.txt'): |
| | ... data = infile.read()[10] |
| | ... except FileNotFoundError: |
| | ... print('File data.txt does not exist') |
| | ... except PermissionError: |
| | ... print('File data.txt is not readable') |
| | ... except IndexError: |
| | ... print('File data.txt is too short') |
| | ... finally: # Clean up |
| | ... if infile: |
| | ... infile.close() |
| | ... |
| | File data.txt is too short |
| | >>> infile.closed |
| | True |
A with block is preferred alternative to try/finally for working with open files. It guarantees that the file is closed when the block ends, even if an exception occurs. A hand-written finally block can fail to close the file if it raises another exception before the close call runs.