In this article, let us learn about printing error messages from Exceptions with the help of 5 specifically chosen examples.
I have divided this article into 2 major sections
- Printing custom error messages and
- Printing a specific part of the default error message. By “default error message“, I mean the error message that you typically get in the command line if you did not catch a given exception)
Depending on which of the 2 options above you are looking for, you can jump to the respective section of the article using the table of content below.
So, let’s begin!
Printing Custom Error messages
There are 3 ways to print custom error messages in Python. Let us start with the simplest of the 3, which is using a print() statement.
Option#1: Using a simple print() statement
The first and easiest option is to print error messages using a simple print() statement as shown in the example below.
try:
#Some Problematic code that can produce Exceptions
x = 5/0
except Exception as e:
print('A problem has occurred from the Problematic code: ', e)
Running this code will give the output below.
A problem has occurred from the Problematic code: division by zero
Here the line “x = 5/0″ in Example 1 above raised a “ZeroDivisionError” which was caught by our except clause and the print() statement printed the default error message which is “division by zero” to the standard output.
One thing to note here is the line “except Exception as e“. This line of code’s function is to catch all possible exceptions, whichever occurs first as an “Exception” object. This object is stored in the variable “e” (line 4), which returns the string ‘division by zero‘ when used with the print() statement (line 5).
To summarize if you wish to print out the default error message along with a custom message use Option#1.
This is the simplest way to print error messages in python. But this option of putting your custom messages into print statements might not work in cases where you might be handling a list of exceptions using a single except clause. If you are not exactly sure how to catch a list of exceptions using a single except clause, I suggest reading my other article in the link below.
Python: 3 Ways to Catch Multiple Exceptions in a single “except” clause
There I have explained the 3 ways through which you can catch a list of exceptions along with tips on when is the right situation to catch each of these exceptions.
Now that we have learned how to print the default string which comes with an exception object, let us next learn how to customize the message that e carried (the string ‘division by zero‘) and replace that with our own custom error message.
Option#2: Using Custom Exception classes to get customized error messages
In Python, you can define your own custom exception classes by inheriting from another Exception class as shown in the code below.
class MyOwnException(Exception):
def __str__(self):
return 'My Own Exception has occurred'
def __repr__(self):
return str(type(self))
try:
raise MyOwnException
except MyOwnException as e:
print(e)
print(repr(e))
How to choose the exception class to inherit from?
In the above example, I have inherited from the Exception class in python, but the recommended practice is to choose a class that closely resembles your use-case.
For example, say you are trying to work with a string type object and you are given a list type object instead, here you should inherit your custom exception from TypeError since this Exception type closely resembles your use case which is “the variable is not of expected type”.
If you are looking for getting an appropriate Exception class to inherit from, I recommend having a look at all the built-in exceptions from the official python page here. For the sake of keeping this example simple, I have chosen the higher-level exception type named “Exception” class to inherit from.
In the code below, we are collecting values from the user and to tell the user that there is an error in the value entered we are using the ValueError class.
class EnteredGarbageError(ValueError):
def __str__(self):
return 'You did not select an option provided!'
try:
options = ['A', 'B', 'C']
x = input('Type A or B or C: ')
if x not in options:
raise EnteredGarbageError
else:
print ('You have chosen: ', x)
except EnteredGarbageError as err:
print(err)
Now that we understand how to choose a class to inherit from, let us next have a look at how to customize the default error messages that these classes return.
How to customize the error message in our custom exception class?
To help us achieve our purpose here which is to print some custom error messages, all objects in python come with 2 methods named __str__ and __repr__. This is pronounced “dunder-str” and “dunder-repr” where “dunder” is short for “double underscore”.
Dunder-str method:
The method __str__ returns a string and this is what the built-in print() function calls whenever we pass it an object to print.
print(object1)
In the line above, python will call the __str__ method of the object and prints out the string returned by that method.
Let us have a look at what python’s official documentation over at python.org has to say about the str method.
In simpler words, the str method returns a human-readable string for logging purposes, and when this information is passed to the built-in function print(), the string it returns gets printed.
So since our implementation of str returns the string “My Own Exception has occurred” this string got printed on the first line of the exception message.
Dunder-repr method:
__repr__ is another method available in all objects in python.
Where it differs from the dunder-str method is the fact that while the __str__ is used for getting a “friendly message”, the __repr__ method is used for getting, a more of a, “formal message”. You can think of str as a text you got from your friends and repr as a notice you got from a legal representative!
The below screenshot from python’s official documentation explains the use of __repr__ method.
Again, in simpler words, repr is typically used to print some “formal” or “official” information about an object in Python
In our Example 2 above, the repr method returned the class name using the built-in type() function.
Next, let us see another variation where we can print different error messages using a single Exception class without making a custom class.
Option#3: Custom Error messages from the raise statement
try:
raise Exception('I wish to print this message')
except Exception as error:
print(error)
Lucky for us, python has made this process incredibly simple! Just pass in the message as an argument to the type of exception you wish to raise and this will print that custom message instead!
In the above code, we are throwing an exception of type “Exception” by calling its constructor and giving the custom message as an argument, which then overrides the default __str__ method to return the string passed in.
If you wish to learn more about raise statement, I suggest reading my other article in the link below
Python: Manually throw/raise an Exception using the “raise” statement
where I have explained 3 ways you can use the raise statement in python and when to use each.
But when to use option 2 and when to use option 3?
On the surface, Option#3 of passing in the custom message may look like it made option#2 of using custom classes useless. But the main reason to use Option#2 is the fact that Option#2 can be used to override more than just the __str__ method.
Let’s next move on to section 2 of this article and look at how to choose a specific part of the default error message (the error printed on the console when you don’t catch an exception) and use that to make our own error messages
Choosing Parts of Default Error Messages to print
To understand what I mean by “Default Error Message” let us see an example
raise ValueError("This is an ValueError")
This line when run, will print the following error message
Traceback (most recent call last):
File "<ipython-input-24-57127e33a735>", line 1, in <module>
raise ValueError("This is an ValueError")
ValueError: This is an ValueError
This error message contains 3 Parts
- Exception Type (ValueError)
- Error message (This is an ValueError)
- and the stack trace (the 1st few lines showing us where exactly in the program the exception has occurred)
The information needed
- to extract and use each of the individual pieces of information listed above and
- when to use what piece of information
is already covered with the help of several examples in my previous article in the link below
Python Exceptions: Getting and Handling Error Messages as strings.
And with that I will end this article!
If you are looking for another interesting read, try the article in the link below.
Exceptions in Python: Everything You Need To Know!
The above article covers all the basics of Exception handling in Python like
- when and how to ignore exceptions
- when and how to retry the problematic code that produced the exception and
- when and how to log the errors
I hope you enjoyed reading this article and got some value from it.
Feel free to share it with your friends and colleagues!
При выполнении заданий к главам вы скорее всего нередко сталкивались с возникновением различных ошибок. На этой главе мы изучим подход, который позволяет обрабатывать ошибки после их возникновения.
Напишем программу, которая будет считать обратные значения для целых чисел из заданного диапазона и выводить их в одну строку с разделителем «;». Один из вариантов кода для решения этой задачи выглядит так:
print(";".join(str(1 / x) for x in range(int(input()), int(input()) + 1)))
Программа получилась в одну строчку за счёт использования списочных выражений. Однако при вводе диапазона чисел, включающем в себя 0 (например, от -1 до 1), программа выдаст следующую ошибку:
ZeroDivisionError: division by zero
В программе произошла ошибка «деление на ноль». Такая ошибка, возникающая при выполнении программы и останавливающая её работу, называется исключением.
Попробуем в нашей программе избавиться от возникновения исключения деления на ноль. Пусть при попадании 0 в диапазон чисел, обработка не производится и выводится сообщение «Диапазон чисел содержит 0». Для этого нужно проверить до списочного выражения наличие нуля в диапазоне:
interval = range(int(input()), int(input()) + 1)
if 0 in interval:
print("Диапазон чисел содержит 0.")
else:
print(";".join(str(1 / x) for x in interval))
Теперь для диапазона, включающего в себя 0, например, от -2 до 2, исключения ZeroDivisionError
не возникнет. Однако при вводе строки, которую невозможно преобразовать в целое число (например, «a»), будет вызвано другое исключение:
ValueError: invalid literal for int() with base 10: 'a'
Произошло исключение ValueError
. Для борьбы с этой ошибкой нам придётся проверить, что строка состоит только из цифр. Сделать это нужно до преобразования в число. Тогда наша программа будет выглядеть так:
start = input()
end = input()
# Метод lstrip("-"), удаляющий символы "-" в начале строки, нужен для учёта
# отрицательных чисел, иначе isdigit() вернёт для них False
if not (start.lstrip("-").isdigit() and end.lstrip("-").isdigit()):
print("Необходимо ввести два числа.")
else:
interval = range(int(start), int(end) + 1)
if 0 in interval:
print("Диапазон чисел содержит 0.")
else:
print(";".join(str(1 / x) for x in interval))
Теперь наша программа работает без ошибок и при вводе строк, которые нельзя преобразовать в целое число.
Подход, который был нами применён для предотвращения ошибок, называется «Look Before You Leap» (LBYL), или «посмотри перед прыжком». В программе, реализующей такой подход, проверяются возможные условия возникновения ошибок до исполнения основного кода.
Подход LBYL имеет недостатки. Программу из примера стало сложнее читать из-за вложенного условного оператора. Проверка условия, что строка может быть преобразована в число, выглядит даже сложнее, чем списочное выражение. Вложенный условный оператор не решает поставленную задачу, а только лишь проверяет входные данные на корректность. Легко заметить, что решение основной задачи заняло меньше времени, чем составление условий проверки корректности входных данных.
Существует другой подход для работы с ошибками: «Easier to Ask Forgiveness than Permission» (EAFP) или «проще извиниться, чем спрашивать разрешение». В этом подходе сначала исполняется код, а в случае возникновения ошибок происходит их обработка. Подход EAFP реализован в Python в виде обработки исключений.
Исключения в Python являются классами ошибок. В Python есть много стандартных исключений. Они имеют определённую иерархию за счёт механизма наследования классов. В документации Python версии 3.10.8 приводится следующее дерево иерархии стандартных исключений:
BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- StopIteration +-- StopAsyncIteration +-- ArithmeticError | +-- FloatingPointError | +-- OverflowError | +-- ZeroDivisionError +-- 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 +-- EncodingWarning +-- ResourceWarning
Для обработки исключения в Python используется следующий синтаксис:
try: <код , который может вызвать исключения при выполнении> except <классисключения_1>: <код обработки исключения> except <классисключения_2>: <код обработки исключения> ... else: <код выполняется, если не вызвано исключение в блоке try> finally: <код , который выполняется всегда>
Блок try
содержит код, в котором нужно обработать исключения, если они возникнут. При возникновении исключения интерпретатор последовательно проверяет в каком из блоков except
обрабатывается это исключение. Исключение обрабатывается в первом блоке except
, обрабатывающем класс этого исключения или базовый класс возникшего исключения. Необходимо учитывать иерархию исключений для определения порядка их обработки в блоках except
. Начинать обработку исключений следует с более узких классов исключений. Если начать с более широкого класса исключения, например, Exception
, то всегда при возникновении исключения будет срабатывать первый блок except
. Сравните два следующих примера. В первом порядок обработки исключений указан от производных классов к базовым, а во втором – наоборот.
try:
print(1 / int(input()))
except ZeroDivisionError:
print("Ошибка деления на ноль.")
except ValueError:
print("Невозможно преобразовать строку в число.")
except Exception:
print("Неизвестная ошибка.")
При вводе значений «0» и «a» получим ожидаемый соответствующий возникающим исключениям вывод:
Невозможно преобразовать строку в число.
и
Ошибка деления на ноль.
Второй пример:
try:
print(1 / int(input()))
except Exception:
print("Неизвестная ошибка.")
except ZeroDivisionError:
print("Ошибка деления на ноль.")
except ValueError:
print("Невозможно преобразовать строку в число.")
При вводе значений «0» и «a» получим в обоих случаях неинформативный вывод:
Неизвестная ошибка.
Необязательный блок else
выполняет код в случае, если в блоке try
не вызвано исключение. Добавим блок else
в пример для вывода сообщения об успешном выполнении операции:
try:
print(1 / int(input()))
except ZeroDivisionError:
print("Ошибка деления на ноль.")
except ValueError:
print("Невозможно преобразовать строку в число.")
except Exception:
print("Неизвестная ошибка.")
else:
print("Операция выполнена успешно.")
Теперь при вводе корректного значения, например, «5», вывод программы будет следующим:
2.0 Операция выполнена успешно.
Блок finally
выполняется всегда, даже если возникло какое-то исключение, не учтённое в блоках except
или код в этих блоках сам вызвал какое-либо исключение. Добавим в нашу программу вывод строки «Программа завершена» в конце программы даже при возникновении исключений:
try:
print(1 / int(input()))
except ZeroDivisionError:
print("Ошибка деления на ноль.")
except ValueError:
print("Невозможно преобразовать строку в число.")
except Exception:
print("Неизвестная ошибка.")
else:
print("Операция выполнена успешно.")
finally:
print("Программа завершена.")
Перепишем код, созданный с применением подхода LBYL, для первого примера из этой главы с использованием обработки исключений:
try:
print(";".join(str(1 / x) for x in range(int(input()), int(input()) + 1)))
except ZeroDivisionError:
print("Диапазон чисел содержит 0.")
except ValueError:
print("Необходимо ввести два числа.")
Теперь наша программа читается намного легче. При этом создание кода для обработки исключений не заняло много времени и не потребовало проверки сложных условий.
Исключения можно принудительно вызывать с помощью оператора raise
. Этот оператор имеет следующий синтаксис:
raise <класс исключения>(параметры)
В качестве параметра можно, например, передать строку с сообщением об ошибке.
В Python можно создавать свои собственные исключения. Синтаксис создания исключения такой же, как и у создания класса. При создании исключения его необходимо наследовать от какого-либо стандартного класса-исключения.
Напишем программу, которая выводит сумму списка целых чисел, и вызывает исключение, если в списке чисел есть хотя бы одно чётное или отрицательное число. Создадим свои классы исключений:
- NumbersError – базовый класс исключения;
- EvenError – исключение, которое вызывается при наличии хотя бы одного чётного числа;
- NegativeError – исключение, которое вызывается при наличии хотя бы одного отрицательного числа.
class NumbersError(Exception):
pass
class EvenError(NumbersError):
pass
class NegativeError(NumbersError):
pass
def no_even(numbers):
if all(x % 2 != 0 for x in numbers):
return True
raise EvenError("В списке не должно быть чётных чисел")
def no_negative(numbers):
if all(x >= 0 for x in numbers):
return True
raise NegativeError("В списке не должно быть отрицательных чисел")
def main():
print("Введите числа в одну строку через пробел:")
try:
numbers = [int(x) for x in input().split()]
if no_negative(numbers) and no_even(numbers):
print(f"Сумма чисел равна: {sum(numbers)}.")
except NumbersError as e: # обращение к исключению как к объекту
print(f"Произошла ошибка: {e}.")
except Exception as e:
print(f"Произошла непредвиденная ошибка: {e}.")
if __name__ == "__main__":
main()
Обратите внимание: в программе основной код выделен в функцию main
. А код вне функций содержит только условный оператор и вызов функции main
при выполнении условия __name__ == "__main__"
. Это условие проверяет, запущен ли файл как самостоятельная программа или импортирован как модуль.
Любая программа, написанная на языке программирования Python может быть импортирована как модуль в другую программу. В идеологии Python импортировать модуль – значит полностью его выполнить. Если основной код модуля содержит вызовы функций, ввод или вывод данных без использования указанного условия __name__ == "__main__"
, то произойдёт полноценный запуск программы. А это не всегда удобно, если из модуля нужна только отдельная функция или какой-либо класс.
При изучении модуля itertools
, мы говорили о том, как импортировать модуль в программу. Покажем ещё раз два способа импорта на примере собственного модуля.
Для импорта модуля из файла, например example_module.py
, нужно указать его имя, если он находится в той же папке, что и импортирующая его программа:
import example_module
Если требуется отдельный компонент модуля, например функция или класс, то импорт можно осуществить так:
from example_module import some_function, ExampleClass
Обратите внимание: при втором способе импортированные объекты попадают в пространство имён новой программы. Это означает, что они будут объектами новой программы, и в программе не должно быть других объектов с такими же именами.
Sometimes, a Python script comes across an unusual situation that it can’t handle, and the program gets terminated or crashed. In this article, we’ll learn How to catch and print the exception messages in python. If you want to learn more about Python Programming, visit Python Tutorials.
The most common method to catch and print the exception message in Python is by using except and try statement. You can also save its error message using this method. Another method is to use logger.exception() which produces an error message as well as the log trace, which contains information such as the code line number at which the exception occurred and the time the exception occurred.
The most common example is a “FileNotFoundError” when you’re importing a file, but it doesn’t exist. Similarly, dividing a number by zero gives a “ZeroDivisionError” and displays a system-generated error message. All these run-time errors are known as exceptions. These exceptions should be caught and reported to prevent the program from being terminated.
In Python, exceptions are handled with the (try… except) statement. The statements which handle the exceptions are placed in the except block whereas the try clause includes the expressions which can raise an exception. Consider an example in which you take a list of integers as input from the user.
Example
# Creating an empty list new_list =[] n = int(input("Enter number of elements : ")) for i in range(n): item = int(input()) # Add the item in the list new_list.append(item) print(new_list)
The program shown above takes integers as input and creates a list of these integers. If the user enters any character, the program will crash and generate the following output.
Output:
Enter number of elements : 7
23
45
34
65
2a
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-1-ac783af2c9a3> in <module>()
3 n = int(input("Enter number of elements : "))
4 for i in range(n):
----> 5 item = int(input())
6 # Add the item in the list
7 new_list.append(item)
ValueError: invalid literal for int() with base 10: '2a'
USE except and try statement to CATCH ANd print the EXCEPTION AND SAVE ITS ERROR MESSAGEs
The first method to catch and print the exception messages in python is by using except and try statement. If the user enters anything except the integer, we want the program to skip that input and move to the next value. In this way, our program will not crash and will catch and print the exception message. This can be done using try and except statements. Inside the try clause, we’ll take input from the user and append it to “new_list” variable. If the user has entered any input except integers mistakenly, the except block will print “Invalid entry” and move towards the next value. In this way, the program continues to run and skip the invalid entries.
# Creating an empty list new_list =[] n = int(input("Enter number of elements : ")) for i in range(n): try: item = int(input()) # Add the item in the list new_list.append(item) except: print("Invalid Input!") print("Next entry.") print("The list entered by user is: ", new_list)
Output:
Enter number of elements : 7
65
43
23
4df
Invalid Input!
Next entry.
76
54
90
The list entered by user is: [65, 43, 23, 76, 54, 90]
There are various methods to catch and report these exceptions using try and except block. Some of them are listed below along with examples.
Catching and reporting/Print exceptions messages in python
This is the second method to catch and print the exception messages in python. With the help of the print function, you can capture, get and print an exception message in Python. Consider an example in which you have a list containing elements of different data types. You want to divide all the integers by any number. This number on division with the string datatypes will raise “TypeError” and the program will terminate if the exceptions are not handled. The example shown below describes how to handle this problem by capturing the exception using the try-except block and reporting it using the print command.
EXAMPLE 3:
list_arr=[76,65,87,"5f","7k",78,69] for elem in list_arr: try: print("Result: ", elem/9) except Exception as e: print("Exception occurred for value '"+ elem + "': "+ repr(e))
Output:
Result: 8.444444444444445
Result: 7.222222222222222
Result: 9.666666666666666
Exception occurred for value '5f': TypeError("unsupported operand type(s) for /: 'str' and 'int'")
Exception occurred for value '7k': TypeError("unsupported operand type(s) for /: 'str' and 'int'")
Result: 8.666666666666666
Result: 7.666666666666667
using try and logger.exception to print an error message
Another method is to use logger.exception() which produces an error message as well as the log trace, which contains information such as the code line number at which the exception occurred and the time the exception occurred. This logger.exception() method should be included within the except statement; otherwise, it will not function properly.
import logging logger=logging.getLogger() num1=int(input("Enter the number 1:")) num2=int(input("Enter the number 2:")) try: print("Result: ", num1/num2) except Exception as e: logger.exception("Exception Occured while code Execution: "+ str(e))
Output:
Enter the number 1:82
Enter the number 2:4
Result: 20.5
Suppose if a user enters 0 in the 2nd number, then this will raise a “ZeroDivisionError” as shown below.
Enter the number 1:9
Enter the number 2:0
Exception Occured while code Execution: division by zero
Traceback (most recent call last):
File "<ipython-input-27-00694f615c2f>", line 11, in <module>
print("Result: ", num1/num2)
ZeroDivisionError: division by zero
Similarly, if you’ve two lists consisting of integers and you want to create a list consisting of results obtained by dividing list1 with list2. Suppose you don’t know whether the two lists consist of integers or not.
EXAMPLE 5:
import logging logger=logging.getLogger() list1=[45, 32, 76, 43, 0, 76] list2=[24, "world", 5, 0, 4, 6] Result=[] for i in range(len(list1)): try: Result.append(list1[i]/list2[i]) except Exception as e: logger.exception("Exception Occured while code Execution: "+ str(e)) print(Result)
Output:
In this example, “world” in the 2nd index of list2 is a string and 32 on division with a string would raise an exception. But, we have handled this exception using try and except block. The logger.exception() command prints the error along with the line at which it occurred and then moves toward the next index. Similarly, all the values are computed and stored in another list which is then displayed at the end of the code.
Exception Occured while code Execution: unsupported operand type(s) for /: 'int' and 'str'
Traceback (most recent call last):
File "<ipython-input-1-5a40f7f6c621>", line 8, in <module>
Result.append(list1[i]/list2[i])
TypeError: unsupported operand type(s) for /: 'int' and 'str'
Exception Occured while code Execution: division by zero
Traceback (most recent call last):
File "<ipython-input-1-5a40f7f6c621>", line 8, in <module>
Result.append(list1[i]/list2[i])
ZeroDivisionError: division by zero
[1.875, 15.2, 0.0, 12.666666666666666]
The logger module has another function “logger.error()” which returns only an error message. The following example demonstrates how the logger.error() function may be used to capture exception messages in Python. In this example, we have just replaced logger.exception in the above example with logger.error() function
EXAMPLE 6:
import logging logger=logging.getLogger() list1=[45, 32,76,43,0, 76] list2=[24, "world", 5, 0, 4, 6] Result=[] for i in range(len(list1)): try: Result.append(list1[i]/list2[i]) except Exception as e: logger.error("Exception Occured while code Execution: "+ str(e)) print(Result)
Output:
Exception Occured while code Execution: unsupported operand type(s) for /: 'int' and 'str'
Exception Occured while code Execution: division by zero
[1.875, 15.2, 0.0, 12.666666666666666]
Catching and printing Specific Exceptions messages
The previous section was all about how to catch and print exceptions. But, how will you catch a specific exception such as Valueerror, ZeroDivisionError, ImportError, etc? There are two cases if you want to catch one specific exception or multiple specific exceptions. The following example shows how to catch a specific exception.
EXAMPLE 7:
a = 'hello' b = 4 try: print(a + b) except TypeError as typo: print(typo)
Output:
can only concatenate str (not "int") to str
Similarly, if you want to print the result of “a/b” also and the user enters 0 as an input in variable “b”, then the same example would not be able to deal with ZeroDivisionError. Therefore we have to use multiple Except clauses as shown below.
EXAMPLE 8:
a = 6 b = 0 try: print(a + b) print(a/b) except TypeError as typo: print(typo) except ZeroDivisionError as zer: print(zer)
Output:
The same code is now able to handle multiple exceptions.
6
division by zero
To summarize, all of the methods described above are effective and efficient. You may use any of the methods listed above to catch and print the exception messages in Python depending on your preferences and level of comfort with the method. If you’ve any queries regarding this article, please let us know in the comment section. Your feedback matters a lot to us.
Prerequisite: Python Traceback
To print stack trace for an exception the suspicious code will be kept in the try block and except block will be employed to handle the exception generated. Here we will be printing the stack trace to handle the exception generated. The printing stack trace for an exception helps in understanding the error and what went wrong with the code. Not just this, the stack trace also shows where the error occurred.
The general structure of a stack trace for an exception:
- Traceback for the most recent call.
- Location of the program.
- Line in the program where the error was encountered.
- Name of the error: relevant information about the exception.
Example:
Traceback (most recent call last): File "C:/Python27/hdg.py", line 5, in value=A[5] IndexError: list index out of range
Method 1: By using print_exc() method.
This method prints exception information and stack trace entries from traceback object tb to file.
Syntax: traceback.print_exc(limit=None, file=None, chain=True)
Parameters: This method accepts the following parameters:
- if a limit argument is positive, Print up to limit stack trace entries from traceback object tb (starting from the caller’s frame). Otherwise, print the last abs(limit) entries. If the limit argument is None, all entries are printed.
- If the file argument is None, the output goes to sys.stderr; otherwise, it should be an open file or file-like object to receive the output.
- If chain argument is true (the default), then chained exceptions will be printed as well, like the interpreter itself does when printing an unhandled exception.
Return: None.
Code:
Python3
import
traceback
A
=
[
1
,
2
,
3
,
4
]
try
:
value
=
A[
5
]
except
:
traceback.print_exc()
print
(
"end of program"
)
Output:
Traceback (most recent call last): File "C:/Python27/hdg.py", line 8, in value=A[5] IndexError: list index out of range end of program
Method 2: By using print_exception() method.
This method prints exception information and stack trace entries from traceback object tb to file.
Syntax : traceback.print_exception(etype, value, tb, limit=None, file=None, chain=True)
Parameters: This method accepts the following parameters:
- if tb argument is not None, it prints a header Traceback (most recent call last):
- it prints the exception etype and value after the stack trace
- if type(value) argument is SyntaxError and value has the appropriate format, it prints the line where the syntax error occurred with a caret indicating the approximate position of the error.
- if a limit argument is positive, Print up to limit stack trace entries from traceback object tb (starting from the caller’s frame). Otherwise, print the last abs(limit) entries. If the limit argument is None, all entries are printed.
- If the file argument is None, the output goes to sys.stderr; otherwise, it should be an open file or file-like object to receive the output.
- If chain argument is true (the default), then chained exceptions will be printed as well, like the interpreter itself does when printing an unhandled exception.
Return: None.
Code:
Python3
import
traceback
import
sys
a
=
4
b
=
0
try
:
value
=
a
/
b
except
:
traceback.print_exception(
*
sys.exc_info())
print
(
"end of program"
)
Output:
Traceback (most recent call last): File "C:/Python27/hdg.py", line 10, in value=a/b ZeroDivisionError: integer division or modulo by zero end of program
Python comes with an extensive support of exceptions and exception handling. An exception event interrupts and, if uncaught, immediately terminates a running program. The most popular examples are the IndexError
, ValueError
, and TypeError
.
An exception will immediately terminate your program. To avoid this, you can catch the exception with a try/except
block around the code where you expect that a certain exception may occur. Here’s how you catch and print a given exception:
To catch and print an exception that occurred in a code snippet, wrap it in an indented try
block, followed by the command "except Exception as e"
that catches the exception and saves its error message in string variable e
. You can now print the error message with "print(e)"
or use it for further processing.
try: # ... YOUR CODE HERE ... # except Exception as e: # ... PRINT THE ERROR MESSAGE ... # print(e)
Example 1: Catch and Print IndexError
If you try to access the list element with index 100 but your lists consist only of three elements, Python will throw an IndexError
telling you that the list index is out of range.
try: lst = ['Alice', 'Bob', 'Carl'] print(lst[3]) except Exception as e: print(e) print('Am I executed?')
Your genius code attempts to access the fourth element in your list with index 3—that doesn’t exist!
Fortunately, you wrapped the code in a try/catch
block and printed the exception. The program is not terminated. Thus, it executes the final print()
statement after the exception has been caught and handled. This is the output of the previous code snippet.
list index out of range Am I executed?
🌍 Recommended Tutorial: How to Print an Error in Python?
Example 2: Catch and Print ValueError
The ValueError
arises if you try to use wrong values in some functions. Here’s an example where the ValueError
is raised because you tried to calculate the square root of a negative number:
import math try: a = math.sqrt(-2) except Exception as e: print(e) print('Am I executed?')
The output shows that not only the error message but also the string 'Am I executed?'
is printed.
math domain error Am I executed?
Example 3: Catch and Print TypeError
Python throws the TypeError object is not subscriptable
if you use indexing with the square bracket notation on an object that is not indexable. This is the case if the object doesn’t define the __getitem__()
method. Here’s how you can catch the error and print it to your shell:
try: variable = None print(variable[0]) except Exception as e: print(e) print('Am I executed?')
The output shows that not only the error message but also the string 'Am I executed?'
is printed.
'NoneType' object is not subscriptable Am I executed?
I hope you’re now able to catch and print your error messages.
Summary
To catch and print an exception that occurred in a code snippet, wrap it in an indented try
block, followed by the command "except Exception as e"
that catches the exception and saves its error message in string variable e
. You can now print the error message with "print(e)"
or use it for further processing.
Where to Go From Here?
Enough theory. Let’s get some practice!
Coders get paid six figures and more because they can solve problems more effectively using machine intelligence and automation.
To become more successful in coding, solve more real problems for real people. That’s how you polish the skills you really need in practice. After all, what’s the use of learning theory that nobody ever needs?
You build high-value coding skills by working on practical coding projects!
Do you want to stop learning with toy projects and focus on practical code projects that earn you money and solve real problems for people?
🚀 If your answer is YES!, consider becoming a Python freelance developer! It’s the best way of approaching the task of improving your Python skills—even if you are a complete beginner.
If you just want to learn about the freelancing opportunity, feel free to watch my free webinar “How to Build Your High-Income Skill Python” and learn how I grew my coding business online and how you can, too—from the comfort of your own home.
Join the free webinar now!
Programmer Humor
Q: How do you tell an introverted computer scientist from an extroverted computer scientist?
A: An extroverted computer scientist looks at your shoes when he talks to you.
While working as a researcher in distributed systems, Dr. Christian Mayer found his love for teaching computer science students.
To help students reach higher levels of Python success, he founded the programming education website Finxter.com. He’s author of the popular programming book Python One-Liners (NoStarch 2020), coauthor of the Coffee Break Python series of self-published books, computer science enthusiast, freelancer, and owner of one of the top 10 largest Python blogs worldwide.
His passions are writing, reading, and coding. But his greatest passion is to serve aspiring coders through Finxter and help them to boost their skills. You can join his free email academy here.
Python exception handling is the process of identifying and responding to errors in a program. In other words, it is a way to deal with errors that might occur in your program. In this article, you will learn how to handle errors in Python by using the Python try
and except
keywords. It will also teach you how to create custom exceptions, which can be used to define your own specific error messages.
Table of Contents
- 1 What is an exception?
- 2 Python try except
- 3 Catching exceptions with try except
- 4 The finally and else blocks
- 5 Common Python exceptions
- 6 Exception best practices
- 7 Create custom exceptions
- 8 Raising (or throwing) exceptions
- 9 How to print a Python exception
- 10 Keep learning
What is an exception?
An exception is a condition that arises during the execution of a program. It is a signal that something unexpected happened. Python represents exceptions by an object of a certain type.
In Python, all built-in, non-system-exiting exceptions are derived from the Exception
class. Exceptions have their own, descriptive names. For example, if you try to divide a number by zero, you will get a ZeroDivisionError
exception, which is also a subclass of the Exception
class.
For a complete hierarchy of all exceptions, you can view the Python manual if you’re interested. Here’s a small excerpt from this hierarchy, just to illustrate:
BaseException +-- SystemExit +-- KeyboardInterrupt +-- Exception +-- ArithmeticError | +-- FloatingPointError | +-- OverflowError | +-- ZeroDivisionError .....
In case your knowledge about objects, classes, and inheritance is a bit rusty, you may want to read my article on objects and classes and my article on inheritance first.
When something unexpected occurs, we can raise an exception at the point of the error. When an exception is raised, Python stops the current flow of execution and starts looking for an exception handler that can handle it. So what is an exception handler? Here’s where the try and except statements come into play.
As demonstrated in the illustration, we can create a block of code by starting with a try statement. This basically means: try to run this code, but an exception might occur.
After our try block, one or more except blocks must follow. This is where the magic happens. These except blocks can catch an exception, as we usually call this. In fact, many other programming languages use a statement called catch
instead of except
. Each except
block can handle a specific type of exception.
Remember: classes are hierarchical. For that reason, exceptions are too. Hence, except blocks must go from most specific, like a ZeroDivisionError
, to less specific, like an ArithmeticError
.
To demonstrate this, imagine what happens when we would start with an except block that catches Exception
. This first block would catch basically everything, because most exceptions inherit from this one, rendering the other except blocks useless.
Now let’s first go back to raising an exception. When an exception is raised, the exception handler that’s able to handle the exception can be nearby, but it can also be in a different module. What’s important to realize, is that Python won’t just scan your code at random for an exception handler. Instead, the handler should be somewhere in the call stack.
Please forget about else
and finally
for now. I’ll explain them in detail further down in this article. We first need to discuss call stacks, to truly understand how an exception finds its way to an exception handler.
Call stack
A call stack is an ordered list of functions that are currently being executed. For example, you might call function A, which calls function B, which calls function C. We now have a call stack consisting of A, B, and C. When C raises an exception, Python will look for an exception handler in this call stack, going backward from end to start. It can be in function C (closest to the exception), in function B (somewhat farther), in function A, or even at the top level of the program where we called function A.
If Python finds a suitable except block, it executes the code in that block. If it doesn’t find one, Python handles the exception by itself. This means it will print the exception and exit the program since it has no clue what to do with it.
I hope you’re still with me! If not, no worries. The examples on this page will hopefully make all this more clear. You might want to revisit this section once you’ve finished the entire article.
Catching exceptions with try except
Let’s finally write some actual code! To handle an exception, we need to catch it. As we just learned, we can catch an exception by using the try
and except
keywords. When an exception occurs while we are inside the try
block, the code in the except
block is executed.
A simple example
Let’s try a simple example first. As you hopefully know, we can’t divide by the number zero. If we do so anyway, Python will throw and exception called ZeroDivisionError
, which is a subclass of ArithmeticError
:
try: print(2/0) except ZeroDivisionError: print("You can't divide by zero!")
If you call a Python function inside the try block, and an exception occurs in that function, the flow of code execution stops at the point of the exception and the code in the except block is executed. Try doing this again, without try and except. You’ll see that Python prints the exception for us. You can do so in the following code crumb:
Also, note that Python prints the error to stderr if you don’t handle the exception by yourself. In the crumb above, this is visible because the output appears in an ‘Error’ tab instead of an ‘Output’ tab.
Catching IOError
Let’s try another, more common example. After all, who divides a number by zero, right?
Exceptions are likely to occur when interacting with the outside world, e.g. when working with files or networks. For example, if you try to open a file with Python, but that file doesn’t exist, you will get an IOError
exception. If you don’t have access to a file due to permissions, you will again get an IOError
exception. Let’s see how to handle these exceptions.
Assignment
Please do the following:
- Run the code below, notice the file name (it doesn’t exist). See what happens.
- Alter the file name to myfile.txt file and run the code again. What happens now?
Alternatively, here’s the code to copy/paste:
try: # Open file in read-only mode with open("not_here.txt", 'r') as f: f.write("Hello World!") except IOError as e: print("An error occurred:", e)
Answers
The file is not found in the first case. You should get this output:
An error occurred: [Errno 2] No such file or directory: 'not_here.txt'
In the second case, you’ll still get an error after creating the file. This time because we’re trying to write to a file that is opened in read-only mode. For more information on these modes, read the article on opening, reading, and writing files with Python. The error should look like this:
An error occurred: not writable
Although this is an error, it’s not written to the stderr output of the operating system. That’s because we handled the exception ourselves. If you removed the try.. except from the code completely and then try to write to the file in read-only mode, Python will catch the error, force the program to terminate, and show this message:
Traceback (most recent call last): File "tryexcept.py", line 3, in <module> f.write("Hello World!") io.UnsupportedOperation: not writable
The finally and else blocks
Remember the other two blocks that I asked you to forget for a while? Let’s look at those now, starting with the finally
block.
The finally block in try-except
The finally
block is executed regardless of whether an exception occurs or not. Finally
blocks are useful, for example, when you want to close a file or a network connection regardless of what happens. After all, you want to clean up resources to prevent memory leaks.
Here’s an example of this at work, in which we open a file without using the with statement
, forcing us to close it ourselves:
try: # Open file in write-mode f = open("myfile.txt", 'w') f.write("Hello World!") except IOError as e: print("An error occurred:", e) finally: print("Closing the file now") f.close()
You can also try this interactive example:
You should see the ‘Closing the file now’ message printed on your screen. Now change the writing mode to ‘r’ instead of ‘w’. You’ll get an error since the file does not exist. Despite this exception, we try to close the file anyway thanks to the finally block. This in turn goes wrong too: a NameError
exception is thrown because the file was never opened, and hence f
does not exist. You can fix this with a nested try.. except NameError
. Try it for yourself.
The else block in try-except
In addition to the except
and finally
blocks, you can add an else block. The else block executes only when no exception occurs. So it differs from the finally block, since finally executes even if an exception occurs.
When should you use the else
block? And why shouldn’t you just add extra code to the try
block? Good questions!
According to the Python manual, the use of the else clause is better than adding additional code to the try clause. But why? The reasoning is that it avoids accidentally catching an exception that wasn’t raised by the code being protected by the try and except statements in the first place. I admit I don’t use else blocks very often myself. Also, I find them somewhat confusing, especially for people coming from other languages.
Common Python exceptions
Some exceptions are so common that you’ll inevitably encounter them. Here are a few of the most common ones:
Exception name | When you’ll encounter it | Example situation that raises the exception |
SyntaxError | Raised when there is an error in Python syntax. If not caught, this exception will cause the Python interpreter to exit. | pritn('test') |
KeyError | Raised when a key is not found in a dictionary. | d = { 'a': 1} |
IndexError | Raised when an index is out of range. | lst = [1, 2, 3] |
KeyboardInterrupt | Raised when the user hits the interrupt key (Ctrl+C) | Pressing control+c |
If you like, you can try to evoke these exceptions intentionally. I’ll promise you, you will encounter these countless times in your Python programming career. Knowing and understanding what they mean and when they occur will greatly help you debug your code.
Exception best practices
Now that we know the mechanics of handling exceptions, there are a couple of best practices I’d like to share with you.
Don’t use blank except blocks
I’ve written about this before in the blog post ‘How not to handle exceptions in Python‘. Don’t use a blank block when you want to catch a broad range of exceptions. By this, I mean something like:
try: ... except: print("An error occurred:")
You might encounter this in code samples on the web. If you do, make a habit of improving the exception handling. Why should you, and how can you improve code like the example above?
All exceptions, including system exceptions, inherit from a class called BaseException
. If an except
clause mentions a particular class, that clause also handles any exception classes derived from that class. An empty except
is equivalent to except BaseException
, hence it will catch all possible exceptions.
So although the syntax is allowed, I don’t recommend it. E.g., you’ll also catch KeyboardInterrupt
and SystemExit
exceptions, which prevent your program from exiting. Instead, use a try block with a list of explicit exceptions that you know you can handle. Or, if you really need to, catch the Exception
base class to handle almost all the regular exceptions, but not the system ones.
If you’re feeling adventurous, you can try to catch all exceptions and see what happens:
from time import sleep while True: try: print("Try and stop me") sleep(1) except: print("Don't stop me now, I'm having such a good time!")
You’ll probably need to close your terminal to stop this program. Now change the except block to catch Exception
. You will still catch almost all exceptions, but the program will exit on system exceptions like KeyboardInterrupt
and SystemExit
:
from time import sleep while True: try: print("Try and stop me") sleep(1) except Exception: print("Something went wrong")
It’s better to ask for forgiveness
In Python, you’ll often see a pattern where people simply try if something works, and if it doesn’t, catch the exception. In other words, it’s better to ask for forgiveness than permission. This is in contrast to other languages, where you preferably ask for permission. E.g., in Java, exceptions can slow down your program and you “ask for permission” by doing checks on an object instead of simply trying.
To make this more concrete: in Python, we often just try to access the key in a dictionary. If the key doesn’t exist, we’ll get an exception and handle it. Suppose we just converted some externally provided JSON to a dictionary, and now start to use it:
import json user_json = '{"name": "John", "age": 39}' user = json.loads(user_json) try: print(user['name']) print(user['age']) print(user['address']) ... except KeyError as e: print("There are missing fields in the user object: ", e) # Properly handle the error ...
This will print the error:
There are missing fields in the user object: 'address'
We could have added three checks (if 'name' in user
, if 'age' in user
, etc.) to make sure that all the fields are there. But this is not a good practice. It potentially introduces a lot of code just to check if keys exist. Instead, we ask for forgiveness in our except block once, which is much cleaner and more readable. And if you worry about performance: exceptions don’t take up that many CPU cycles in Python. Lots of comparisons are in fact slower than catching a single exception (if it occurs at all!).
Create custom exceptions
All built-in, non-system-exiting exceptions are derived from the Exception
class as we learned before. All user-defined exceptions should also be derived from this class. So if we want to create our own exceptions, we need to create a subclass of the Exception
class.
For example, if you want to create an exception that indicates that a user was not found, you can create a UserNotFoundError
exception. This would, in its most basic form, look like this:
class UserNotFoundError(Exception): pass
This inherits all the properties and methods of Exception
, but we give it a new name to distinguish it from the Exception
class. This way, we’ll be able to specifically catch it with an except block.
The name of this exception clearly tells us the type of problem that was encountered, so as an added bonus, it functions as a form of code documentation as well. Just like well-named variables and functions, a well-named exception can be a big difference when reading back your code.
We’ll use this class in the example that follows.
Raising (or throwing) exceptions
We know some built-in exceptions and how to create custom exceptions. We also know how to catch exceptions with try and except. What’s left, is what’s called raising or throwing an exception. You can raise an exception yourself with the raise keyword.
In the example below, we use your previously defined UserNotFoundError
. We call a function, fetch_user
, that fetches some user data from an imaginary database. If the user is not found, this database returns None. We decided that we don’t want to return None, which would force the caller to check for None every time. Instead, we use our custom UserNotFoundError
.
class UserNotFoundError(Exception): pass def fetch_user(user_id): # Here you would fetch from some kind of db, e.g.: # user = db.get_user(user_id) # To make this example runnable, let's set it to None user = None if user == None: raise UserNotFoundError(f'User {user_id} not in database') else: return user users = [123, 456, 789] for user_id in users: try: fetch_user(user_id) except UserNotFoundError as e: print("There was an error: ", e)
Assignment
Here’s a little assignment. We could have used a regular Exception object instead. That way, we don’t need to define a custom one. Why is this a bad idea?
Answer
You can in fact raise a regular exception, e.g. with raise Exception('User not found')
. But if you do, you need to catch all exceptions of type Exception. And as we know, there are a lot of those. Chances are you inadvertently catch some other exception that you’re not able to handle. For example, the database client might throw a DatabaseAuthenticationError
which is also a subclass of Exception
.
How to print a Python exception
You can print exceptions directly as long as you catch them properly. You may have seen examples of this above already. To be clear, here’s an example of how to catch and print an exception:
try: ... except Exception as e: print("There was an error: ", e)
If you’d like to print the call stack, just like Python does when you don’t catch the exception yourself, you can import the traceback module:
import traceback try: ... except Exception: traceback.print_exc()
Keep learning
Here are some more resources to deepen your knowledge:
- My blog article ‘How not to handle exceptions in Python‘
- Introduction to Python functions
- Objects and classes, and Python inheritance
- The official documentation on exceptions.
- The official documentation on errors.
Are you enjoying this free tutorial? Please also have a look at my premium courses. They offer a superior user experience with small, easy-to-digest lessons and topics, progress tracking, quizzes to test your knowledge, and practice sessions. Each course will earn you a downloadable course certificate.
Until now error messages haven’t been more than mentioned, but if you have tried
out the examples you have probably seen some. There are (at least) two
distinguishable kinds of errors: syntax errors and exceptions.
8.1. Syntax Errors¶
Syntax errors, also known as parsing errors, are perhaps the most common kind of
complaint you get while you are still learning Python:
>>> while True print('Hello world') File "<stdin>", line 1 while True print('Hello world') ^ SyntaxError: invalid syntax
The parser repeats the offending line and displays a little ‘arrow’ pointing at
the earliest point in the line where the error was detected. The error is
caused by (or at least detected at) the token preceding the arrow: in the
example, the error is detected at the function print()
, since a colon
(':'
) is missing before it. File name and line number are printed so you
know where to look in case the input came from a script.
8.2. Exceptions¶
Even if a statement or expression is syntactically correct, it may cause an
error when an attempt is made to execute it. Errors detected during execution
are called exceptions and are not unconditionally fatal: you will soon learn
how to handle them in Python programs. Most exceptions are not handled by
programs, however, and result in error messages as shown here:
>>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero >>> 4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't convert 'int' object to str implicitly
The last line of the error message indicates what happened. Exceptions come in
different types, and the type is printed as part of the message: the types in
the example are ZeroDivisionError
, NameError
and TypeError
.
The string printed as the exception type is the name of the built-in exception
that occurred. This is true for all built-in exceptions, but need not be true
for user-defined exceptions (although it is a useful convention). Standard
exception names are built-in identifiers (not reserved keywords).
The rest of the line provides detail based on the type of exception and what
caused it.
The preceding part of the error message shows the context where the exception
happened, in the form of a stack traceback. In general it contains a stack
traceback listing source lines; however, it will not display lines read from
standard input.
Built-in Exceptions lists the built-in exceptions and their meanings.
8.3. Handling Exceptions¶
It is possible to write programs that handle selected exceptions. Look at the
following example, which asks the user for input until a valid integer has been
entered, but allows the user to interrupt the program (using Control-C
or
whatever the operating system supports); note that a user-generated interruption
is signalled by raising the KeyboardInterrupt
exception.
>>> while True: ... try: ... x = int(input("Please enter a number: ")) ... break ... except ValueError: ... print("Oops! That was no valid number. Try again...") ...
The try
statement works as follows.
- First, the try clause (the statement(s) between the
try
and
except
keywords) is executed. - If no exception occurs, the except clause is skipped and execution of the
try
statement is finished. - If an exception occurs during execution of the try clause, the rest of the
clause is skipped. Then if its type matches the exception named after the
except
keyword, the except clause is executed, and then execution
continues after thetry
statement. - If an exception occurs which does not match the exception named in the except
clause, it is passed on to outertry
statements; if no handler is
found, it is an unhandled exception and execution stops with a message as
shown above.
A try
statement may have more than one except clause, to specify
handlers for different exceptions. At most one handler will be executed.
Handlers only handle exceptions that occur in the corresponding try clause, not
in other handlers of the same try
statement. An except clause may
name multiple exceptions as a parenthesized tuple, for example:
... except (RuntimeError, TypeError, NameError): ... pass
A class in an except
clause is compatible with an exception if it is
the same class or a base class thereof (but not the other way around — an
except clause listing a derived class is not compatible with a base class). For
example, the following code will print B, C, D in that order:
class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B")
Note that if the except clauses were reversed (with except B
first), it
would have printed B, B, B — the first matching except clause is triggered.
The last except clause may omit the exception name(s), to serve as a wildcard.
Use this with extreme caution, since it is easy to mask a real programming error
in this way! It can also be used to print an error message and then re-raise
the exception (allowing a caller to handle the exception as well):
import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise
The try
… except
statement has an optional else
clause, which, when present, must follow all except clauses. It is useful for
code that must be executed if the try clause does not raise an exception. For
example:
for arg in sys.argv[1:]: try: f = open(arg, 'r') except OSError: print('cannot open', arg) else: print(arg, 'has', len(f.readlines()), 'lines') f.close()
The use of the else
clause is better than adding additional code to
the try
clause because it avoids accidentally catching an exception
that wasn’t raised by the code being protected by the try
…
except
statement.
When an exception occurs, it may have an associated value, also known as the
exception’s argument. The presence and type of the argument depend on the
exception type.
The except clause may specify a variable after the exception name. The
variable is bound to an exception instance with the arguments stored in
instance.args
. For convenience, the exception instance defines
__str__()
so the arguments can be printed directly without having to
reference .args
. One may also instantiate an exception first before
raising it and add any attributes to it as desired.
>>> try: ... raise Exception('spam', 'eggs') ... except Exception as inst: ... print(type(inst)) # the exception instance ... print(inst.args) # arguments stored in .args ... print(inst) # __str__ allows args to be printed directly, ... # but may be overridden in exception subclasses ... x, y = inst.args # unpack args ... print('x =', x) ... print('y =', y) ... <class 'Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs
If an exception has arguments, they are printed as the last part (‘detail’) of
the message for unhandled exceptions.
Exception handlers don’t just handle exceptions if they occur immediately in the
try clause, but also if they occur inside functions that are called (even
indirectly) in the try clause. For example:
>>> def this_fails(): ... x = 1/0 ... >>> try: ... this_fails() ... except ZeroDivisionError as err: ... print('Handling run-time error:', err) ... Handling run-time error: division by zero
8.4. Raising Exceptions¶
The raise
statement allows the programmer to force a specified
exception to occur. For example:
>>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: HiThere
The sole argument to raise
indicates the exception to be raised.
This must be either an exception instance or an exception class (a class that
derives from Exception
). If an exception class is passed, it will
be implicitly instantiated by calling its constructor with no arguments:
raise ValueError # shorthand for 'raise ValueError()'
If you need to determine whether an exception was raised but don’t intend to
handle it, a simpler form of the raise
statement allows you to
re-raise the exception:
>>> try: ... raise NameError('HiThere') ... except NameError: ... print('An exception flew by!') ... raise ... An exception flew by! Traceback (most recent call last): File "<stdin>", line 2, in <module> NameError: HiThere
8.5. User-defined Exceptions¶
Programs may name their own exceptions by creating a new exception class (see
Classes for more about Python classes). Exceptions should typically
be derived from the Exception
class, either directly or indirectly.
Exception classes can be defined which do anything any other class can do, but
are usually kept simple, often only offering a number of attributes that allow
information about the error to be extracted by handlers for the exception. When
creating a module that can raise several distinct errors, a common practice is
to create a base class for exceptions defined by that module, and subclass that
to create specific exception classes for different error conditions:
class Error(Exception): """Base class for exceptions in this module.""" pass class InputError(Error): """Exception raised for errors in the input. Attributes: expression -- input expression in which the error occurred message -- explanation of the error """ def __init__(self, expression, message): self.expression = expression self.message = message class TransitionError(Error): """Raised when an operation attempts a state transition that's not allowed. Attributes: previous -- state at beginning of transition next -- attempted new state message -- explanation of why the specific transition is not allowed """ def __init__(self, previous, next, message): self.previous = previous self.next = next self.message = message
Most exceptions are defined with names that end in “Error,” similar to the
naming of the standard exceptions.
Many standard modules define their own exceptions to report errors that may
occur in functions they define. More information on classes is presented in
chapter Classes.
8.6. Defining Clean-up Actions¶
The try
statement has another optional clause which is intended to
define clean-up actions that must be executed under all circumstances. For
example:
>>> try: ... raise KeyboardInterrupt ... finally: ... print('Goodbye, world!') ... Goodbye, world! KeyboardInterrupt Traceback (most recent call last): File "<stdin>", line 2, in <module>
A finally clause is always executed before leaving the try
statement, whether an exception has occurred or not. When an exception has
occurred in the try
clause and has not been handled by an
except
clause (or it has occurred in an except
or
else
clause), it is re-raised after the finally
clause has
been executed. The finally
clause is also executed “on the way out”
when any other clause of the try
statement is left via a
break
, continue
or return
statement. A more
complicated example:
>>> def divide(x, y): ... try: ... result = x / y ... except ZeroDivisionError: ... print("division by zero!") ... else: ... print("result is", result) ... finally: ... print("executing finally clause") ... >>> divide(2, 1) result is 2.0 executing finally clause >>> divide(2, 0) division by zero! executing finally clause >>> divide("2", "1") executing finally clause Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in divide TypeError: unsupported operand type(s) for /: 'str' and 'str'
As you can see, the finally
clause is executed in any event. The
TypeError
raised by dividing two strings is not handled by the
except
clause and therefore re-raised after the finally
clause has been executed.
In real world applications, the finally
clause is useful for
releasing external resources (such as files or network connections), regardless
of whether the use of the resource was successful.
8.7. Predefined Clean-up Actions¶
Some objects define standard clean-up actions to be undertaken when the object
is no longer needed, regardless of whether or not the operation using the object
succeeded or failed. Look at the following example, which tries to open a file
and print its contents to the screen.
for line in open("myfile.txt"): print(line, end="")
The problem with this code is that it leaves the file open for an indeterminate
amount of time after this part of the code has finished executing.
This is not an issue in simple scripts, but can be a problem for larger
applications. The with
statement allows objects like files to be
used in a way that ensures they are always cleaned up promptly and correctly.
with open("myfile.txt") as f: for line in f: print(line, end="")
After the statement is executed, the file f is always closed, even if a
problem was encountered while processing the lines. Objects which, like files,
provide predefined clean-up actions will indicate this in their documentation.