Книга: Practical Programming, Fourth Edition
Назад: Chapter 17: Handling Exceptions
Дальше: Accessing Exception Details

Core Idioms

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.

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.

Keep “try” blocks small and “except” blocks specific. Separate problem-solving from error handling

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

icon indicating an aside

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.

A “finally” block ensures that your program always cleans up resources, no matter what happens

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.

Назад: Chapter 17: Handling Exceptions
Дальше: Accessing Exception Details