Exceptions are a solution to a difficult problem: How can programs deal with unexpected errors? For instance, what happens if a file disappears in the middle of being read because some other program on your computer has deleted it? Or what if the Web site your program is downloading pages from suddenly crashes?
In these and many other situations, what Python does is raise an exception. An exception is a special kind of error object that you can catch and then examine in order to determine how to handle the error.
Exceptions can change the flow of control of your program. Depending on when it occurs, an exception can cause the flow of control to jump out of the middle of a function or loop into another block of code that does error handling.
Often, you cannot be sure exactly which line might raise an exception, and this creates some tricky problems. Thus Python provides special exception-handling constructs for both catching exceptions and executing clean-up code whether or not an exception is raised.
An example of an exception is IOError
, which is raised when you try to open a file that doesn’t exist:
>>> open('unicorn.dat')
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
open('unicorn.dat')
File "C:Python30libio.py", line 284, in __new__
return open(*args, **kwargs)
File "C:Python30libio.py", line 223, in open
closefd)
IOError: [Errno 2] No such file or directory: 'unicorn.dat'
When an exception is raised and is not caught or handled in any way, Python immediately halts the program and outputs a traceback, which is a list of the functions that were called before the exception occurred. This can be quite useful in pinning down exactly what line causes an error.
The last line of the traceback indicates that an IOError
exception has been raised, and, specifically, it means that unicorn.dat
could not be found in the current working directory. The error message given by an IOError
differs depending on the exact reason for the exception.
As we saw with the open
function, Python’s built-in functions and library functions usually raise exceptions when something unexpected happens.
For instance, dividing by zero throws an exception:
>>> 1/0
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
1/0
ZeroDivisionError: int division or modulo by zero
Syntax errors can also cause exceptions in Python:
>>> x := 5 SyntaxError: invalid syntax (<pyshell#2>, line 1) >>> print('hello world) SyntaxError: EOL while scanning string literal (<pyshell#3>, line 1)
You can also intentionally raise an exception anywhere in your code using the raise
statement. For example:
>>> raise IOError('This is a test!')
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
raise IOError('This is a test!')
IOError: This is a test!
Python has numerous built-in exceptions organized into a hierarchy: See the Python documentation (http://docs.python.org/dev/3.0/library/exceptions.html#bltin-exceptions) for more details.
You have essentially two options for dealing with a raised exception:
Ignore the exception and let your program crash with a traceback. This is usually what you want when you are developing a program, since the traceback provides helpful debugging information.
Catch the exception and print a friendly error message, or possibly even try to fix the problem. This is almost always what you want to do with a program meant to be used by non-programmers: Regular users don’t want to deal with tracebacks!
Here’s an example of how to catch an exception. Suppose you want to read an integer from the user, prompting repeatedly until a valid integer is entered:
def get_age(): while True: try: n = int(input('How old are you? ')) return n except ValueError: print('Please enter an integer value.')
Inside this function’s while-loop is a try/except block. You put whatever code you like in the try
part of the block, with the understanding that one or more lines of that code might raise an exception.
If any line of the try
block does raise an exception, then the flow of control immediately jumps to the except
block, skipping over any statements that have not been executed yet. In this example, the return
statement will be skipped when an exception is raised.
If the try
block raises no exceptions, then the except ValueError
block is ignored and not executed.
So in this example, the int()
function raises a ValueError
if the user enters a string that is not a valid integer. When that happens, the flow of control jumps to the except ValueError
block and prints the error message. When a ValueError
is raised, the return
statement is skipped—the flow of control jumps immediately to the except
block.
If the user enters a valid integer, then no exception is raised, and Python proceeds to the following return
statement, thus ending the function.
Try/except blocks work a little bit like if-statements. However, they are different in an important way: If-statements decide what to do based on the evaluation of Boolean expressions, while try/except blocks decide what to do based on whether or not an exception is raised.
A function can raise more than one kind of exception, and it can even raise the same type of exception for different reasons. Look at these three different int()
exceptions (the tracebacks have been trimmed for readability):
>>> int('two') ValueError: invalid literal for int() with base 10: 'two' >>> int(2, 10) TypeError: int() can't convert non-string with explicit base >>> int('2', 1) ValueError: int() arg 2 must be >= 2 and <= 36
So int()
raises ValueError
for at least two different reasons, while it raises TypeError
in at least one other case.
You can write try/except blocks to handle multiple exceptions. For example, you can group together multiple exceptions in the except
clause:
def convert_to_int1(s, base): try: return int(s, base) except (ValueError, TypeError): return 'error'
Or, if you care about the specific exception that is thrown, you can add extra except
clauses:
def convert_to_int2(s, base): try: return int(s, base) except ValueError: return 'value error' except TypeError: return 'type error'
If you write an except
clause without any exception name, it will catch any and all exceptions:
def convert_to_int3(s, base): try: return int(s, base) except: return 'error'
This form of except
clause will catch any exception—it doesn’t care about what kind of error has occurred, just that one has occurred. In many situations, this is all you need.
A finally
code block can be added to any try/except block to perform clean-up actions. For example:
def invert(x): try: return 1 / x except ZeroDivisionError: return 'error' finally: print('invert(%s) done' % x)
The code block underneath finally
will always be executed after the try
block or the except
block. This is quite useful when you have code that you want to perform regardless of whether an exception is raised. For instance, file close statements are often put in finally
clauses so that files are guaranteed to be closed, even if an unexpected IOError
occurs.
Python’s with
statement is another way to ensure that clean-up actions (such as closing a file) are done as soon as possible, even if there is an exception. For example, consider this code, which prints a file to the screen with numbers for each line:
num = 1 f = open(fname) for line in f: print('%04d %s' % (num, line), end = '') num = num + 1 # following code
What’s unknown here is when the file object f
is closed. At some point after the for-loop, f
will usually be closed. But we don’t know when precisely that will happen: It will remain unclosed but unused for an indeterminate amount of time, which might be a problem if other programs try to access the file.
To ensure that the file is closed as soon as it is no longer needed, use a with
statement:
num = 1 with open(fname, 'r') as f: for line in f: print('%04d %s' % (num, line), end = '') num = num + 1
The onscreen results are the same as the previous code, but when you use a with
statement, the file objects’ clean-up action (that is to say, closing the file) is automatically called as soon as the for-loop ends. Thus f
does not sit around unclosed.
3.145.66.126