Another way hasn’t been given yet:
try:
1/0
except Exception, e:
print e.message
Output:
integer division or modulo by zero
args[0]
might actually not be a message.
str(e)
might return the string with surrounding quotes and possibly with the leading u
if unicode:
'integer division or modulo by zero'
repr(e)
gives the full exception representation which is not probably what you want:
"ZeroDivisionError('integer division or modulo by zero',)"
edit
My bad !!! It seems that BaseException.message
has been deprecated from 2.6
, finally, it definitely seems that there is still not a standardized way to display exception messages. So I guess the best is to do deal with e.args
and str(e)
depending on your needs (and possibly e.message
if the lib you are using is relying on that mechanism).
For instance, with pygraphviz
, e.message
is the only way to display correctly the exception, using str(e)
will surround the message with u''
.
But with MySQLdb
, the proper way to retrieve the message is e.args[1]
: e.message
is empty, and str(e)
will display '(ERR_CODE, "ERR_MSG")'
Содержание:развернуть
- Как устроен механизм исключений
- Как обрабатывать исключения в Python (try except)
-
As — сохраняет ошибку в переменную
-
Finally — выполняется всегда
-
Else — выполняется когда исключение не было вызвано
-
Несколько блоков except
-
Несколько типов исключений в одном блоке except
-
Raise — самостоятельный вызов исключений
-
Как пропустить ошибку
- Исключения в lambda функциях
- 20 типов встроенных исключений в Python
- Как создать свой тип Exception
Программа, написанная на языке Python, останавливается сразу как обнаружит ошибку. Ошибки могут быть (как минимум) двух типов:
- Синтаксические ошибки — возникают, когда написанное выражение не соответствует правилам языка (например, написана лишняя скобка);
- Исключения — возникают во время выполнения программы (например, при делении на ноль).
Синтаксические ошибки исправить просто (если вы используете IDE, он их подсветит). А вот с исключениями всё немного сложнее — не всегда при написании программы можно сказать возникнет или нет в данном месте исключение. Чтобы приложение продолжило работу при возникновении проблем, такие ошибки нужно перехватывать и обрабатывать с помощью блока try/except
.
Как устроен механизм исключений
В Python есть встроенные исключения, которые появляются после того как приложение находит ошибку. В этом случае текущий процесс временно приостанавливается и передает ошибку на уровень вверх до тех пор, пока она не будет обработано. Если ошибка не будет обработана, программа прекратит свою работу (а в консоли мы увидим Traceback с подробным описанием ошибки).
💁♂️ Пример: напишем скрипт, в котором функция ожидает число, а мы передаём сроку (это вызовет исключение «TypeError»):
def b(value):
print("-> b")
print(value + 1) # ошибка тут
def a(value):
print("-> a")
b(value)
a("10")
> -> a
> -> b
> Traceback (most recent call last):
> File "test.py", line 11, in <module>
> a("10")
> File "test.py", line 8, in a
> b(value)
> File "test.py", line 3, in b
> print(value + 1)
> TypeError: can only concatenate str (not "int") to str
В данном примере мы запускаем файл «test.py» (через консоль). Вызывается функция «a«, внутри которой вызывается функция «b«. Все работает хорошо до сточки print(value + 1)
. Тут интерпретатор понимает, что нельзя конкатенировать строку с числом, останавливает выполнение программы и вызывает исключение «TypeError».
Далее ошибка передается по цепочке в обратном направлении: «b» → «a» → «test.py«. Так как в данном примере мы не позаботились обработать эту ошибку, вся информация по ошибке отобразится в консоли в виде Traceback.
Traceback (трассировка) — это отчёт, содержащий вызовы функций, выполненные в определенный момент. Трассировка помогает узнать, что пошло не так и в каком месте это произошло.
Traceback лучше читать снизу вверх ↑
В нашем примере Traceback
содержится следующую информацию (читаем снизу вверх):
TypeError
— тип ошибки (означает, что операция не может быть выполнена с переменной этого типа);can only concatenate str (not "int") to str
— подробное описание ошибки (конкатенировать можно только строку со строкой);- Стек вызова функций (1-я линия — место, 2-я линия — код). В нашем примере видно, что в файле «test.py» на 11-й линии был вызов функции «a» со строковым аргументом «10». Далее был вызов функции «b».
print(value + 1)
это последнее, что было выполнено — тут и произошла ошибка. most recent call last
— означает, что самый последний вызов будет отображаться последним в стеке (в нашем примере последним выполнилсяprint(value + 1)
).
В Python ошибку можно перехватить, обработать, и продолжить выполнение программы — для этого используется конструкция try ... except ...
.
Как обрабатывать исключения в Python (try except)
В Python исключения обрабатываются с помощью блоков try/except
. Для этого операция, которая может вызвать исключение, помещается внутрь блока try
. А код, который должен быть выполнен при возникновении ошибки, находится внутри except
.
Например, вот как можно обработать ошибку деления на ноль:
try:
a = 7 / 0
except:
print('Ошибка! Деление на 0')
Здесь в блоке try
находится код a = 7 / 0
— при попытке его выполнить возникнет исключение и выполнится код в блоке except
(то есть будет выведено сообщение «Ошибка! Деление на 0»). После этого программа продолжит свое выполнение.
💭 PEP 8 рекомендует, по возможности, указывать конкретный тип исключения после ключевого слова except
(чтобы перехватывать и обрабатывать конкретные исключения):
try:
a = 7 / 0
except ZeroDivisionError:
print('Ошибка! Деление на 0')
Однако если вы хотите перехватывать все исключения, которые сигнализируют об ошибках программы, используйте тип исключения Exception
:
try:
a = 7 / 0
except Exception:
print('Любая ошибка!')
As — сохраняет ошибку в переменную
Перехваченная ошибка представляет собой объект класса, унаследованного от «BaseException». С помощью ключевого слова as
можно записать этот объект в переменную, чтобы обратиться к нему внутри блока except
:
try:
file = open('ok123.txt', 'r')
except FileNotFoundError as e:
print(e)
> [Errno 2] No such file or directory: 'ok123.txt'
В примере выше мы обращаемся к объекту класса «FileNotFoundError» (при выводе на экран через print
отобразится строка с полным описанием ошибки).
У каждого объекта есть поля, к которым можно обращаться (например если нужно логировать ошибку в собственном формате):
import datetime
now = datetime.datetime.now().strftime("%d-%m-%Y %H:%M:%S")
try:
file = open('ok123.txt', 'r')
except FileNotFoundError as e:
print(f"{now} [FileNotFoundError]: {e.strerror}, filename: {e.filename}")
> 20-11-2021 18:42:01 [FileNotFoundError]: No such file or directory, filename: ok123.txt
Finally — выполняется всегда
При обработке исключений можно после блока try
использовать блок finally
. Он похож на блок except
, но команды, написанные внутри него, выполняются обязательно. Если в блоке try
не возникнет исключения, то блок finally
выполнится так же, как и при наличии ошибки, и программа возобновит свою работу.
Обычно try/except
используется для перехвата исключений и восстановления нормальной работы приложения, а try/finally
для того, чтобы гарантировать выполнение определенных действий (например, для закрытия внешних ресурсов, таких как ранее открытые файлы).
В следующем примере откроем файл и обратимся к несуществующей строке:
file = open('ok.txt', 'r')
try:
lines = file.readlines()
print(lines[5])
finally:
file.close()
if file.closed:
print("файл закрыт!")
> файл закрыт!
> Traceback (most recent call last):
> File "test.py", line 5, in <module>
> print(lines[5])
> IndexError: list index out of range
Даже после исключения «IndexError», сработал код в секции finally
, который закрыл файл.
p.s. данный пример создан для демонстрации, в реальном проекте для работы с файлами лучше использовать менеджер контекста with.
Также можно использовать одновременно три блока try/except/finally
. В этом случае:
- в
try
— код, который может вызвать исключения; - в
except
— код, который должен выполниться при возникновении исключения; - в
finally
— код, который должен выполниться в любом случае.
def sum(a, b):
res = 0
try:
res = a + b
except TypeError:
res = int(a) + int(b)
finally:
print(f"a = {a}, b = {b}, res = {res}")
sum(1, "2")
> a = 1, b = 2, res = 3
Else — выполняется когда исключение не было вызвано
Иногда нужно выполнить определенные действия, когда код внутри блока try
не вызвал исключения. Для этого используется блок else
.
Допустим нужно вывести результат деления двух чисел и обработать исключения в случае попытки деления на ноль:
b = int(input('b = '))
c = int(input('c = '))
try:
a = b / c
except ZeroDivisionError:
print('Ошибка! Деление на 0')
else:
print(f"a = {a}")
> b = 10
> c = 1
> a = 10.0
В этом случае, если пользователь присвоит переменной «с» ноль, то появится исключение и будет выведено сообщение «‘Ошибка! Деление на 0′», а код внутри блока else
выполняться не будет. Если ошибки не будет, то на экране появятся результаты деления.
Несколько блоков except
В программе может возникнуть несколько исключений, например:
- Ошибка преобразования введенных значений к типу
float
(«ValueError»); - Деление на ноль («ZeroDivisionError»).
В Python, чтобы по-разному обрабатывать разные типы ошибок, создают несколько блоков except
:
try:
b = float(input('b = '))
c = float(input('c = '))
a = b / c
except ZeroDivisionError:
print('Ошибка! Деление на 0')
except ValueError:
print('Число введено неверно')
else:
print(f"a = {a}")
> b = 10
> c = 0
> Ошибка! Деление на 0
> b = 10
> c = питон
> Число введено неверно
Теперь для разных типов ошибок есть свой обработчик.
Несколько типов исключений в одном блоке except
Можно также обрабатывать в одном блоке except сразу несколько исключений. Для этого они записываются в круглых скобках, через запятую сразу после ключевого слова except
. Чтобы обработать сообщения «ZeroDivisionError» и «ValueError» в одном блоке записываем их следующим образом:
try:
b = float(input('b = '))
c = float(input('c = '))
a = b / c
except (ZeroDivisionError, ValueError) as er:
print(er)
else:
print('a = ', a)
При этом переменной er
присваивается объект того исключения, которое было вызвано. В результате на экран выводятся сведения о конкретной ошибке.
Raise — самостоятельный вызов исключений
Исключения можно генерировать самостоятельно — для этого нужно запустить оператор raise
.
min = 100
if min > 10:
raise Exception('min must be less than 10')
> Traceback (most recent call last):
> File "test.py", line 3, in <module>
> raise Exception('min value must be less than 10')
> Exception: min must be less than 10
Перехватываются такие сообщения точно так же, как и остальные:
min = 100
try:
if min > 10:
raise Exception('min must be less than 10')
except Exception:
print('Моя ошибка')
> Моя ошибка
Кроме того, ошибку можно обработать в блоке except
и пробросить дальше (вверх по стеку) с помощью raise
:
min = 100
try:
if min > 10:
raise Exception('min must be less than 10')
except Exception:
print('Моя ошибка')
raise
> Моя ошибка
> Traceback (most recent call last):
> File "test.py", line 5, in <module>
> raise Exception('min must be less than 10')
> Exception: min must be less than 10
Как пропустить ошибку
Иногда ошибку обрабатывать не нужно. В этом случае ее можно пропустить с помощью pass
:
try:
a = 7 / 0
except ZeroDivisionError:
pass
Исключения в lambda функциях
Обрабатывать исключения внутри lambda функций нельзя (так как lambda записывается в виде одного выражения). В этом случае нужно использовать именованную функцию.
20 типов встроенных исключений в Python
Иерархия классов для встроенных исключений в Python выглядит так:
BaseException
SystemExit
KeyboardInterrupt
GeneratorExit
Exception
ArithmeticError
AssertionError
...
...
...
ValueError
Warning
Все исключения в Python наследуются от базового BaseException
:
SystemExit
— системное исключение, вызываемое функциейsys.exit()
во время выхода из приложения;KeyboardInterrupt
— возникает при завершении программы пользователем (чаще всего при нажатии клавиш Ctrl+C);GeneratorExit
— вызывается методомclose
объектаgenerator
;Exception
— исключения, которые можно и нужно обрабатывать (предыдущие были системными и их трогать не рекомендуется).
От Exception
наследуются:
1 StopIteration
— вызывается функцией next в том случае если в итераторе закончились элементы;
2 ArithmeticError
— ошибки, возникающие при вычислении, бывают следующие типы:
FloatingPointError
— ошибки при выполнении вычислений с плавающей точкой (встречаются редко);OverflowError
— результат вычислений большой для текущего представления (не появляется при операциях с целыми числами, но может появиться в некоторых других случаях);ZeroDivisionError
— возникает при попытке деления на ноль.
3 AssertionError
— выражение, используемое в функции assert
неверно;
4 AttributeError
— у объекта отсутствует нужный атрибут;
5 BufferError
— операция, для выполнения которой требуется буфер, не выполнена;
6 EOFError
— ошибка чтения из файла;
7 ImportError
— ошибка импортирования модуля;
8 LookupError
— неверный индекс, делится на два типа:
IndexError
— индекс выходит за пределы диапазона элементов;KeyError
— индекс отсутствует (для словарей, множеств и подобных объектов);
9 MemoryError
— память переполнена;
10 NameError
— отсутствует переменная с данным именем;
11 OSError
— исключения, генерируемые операционной системой:
ChildProcessError
— ошибки, связанные с выполнением дочернего процесса;ConnectionError
— исключения связанные с подключениями (BrokenPipeError, ConnectionResetError, ConnectionRefusedError, ConnectionAbortedError);FileExistsError
— возникает при попытке создания уже существующего файла или директории;FileNotFoundError
— генерируется при попытке обращения к несуществующему файлу;InterruptedError
— возникает в том случае если системный вызов был прерван внешним сигналом;IsADirectoryError
— программа обращается к файлу, а это директория;NotADirectoryError
— приложение обращается к директории, а это файл;PermissionError
— прав доступа недостаточно для выполнения операции;ProcessLookupError
— процесс, к которому обращается приложение не запущен или отсутствует;TimeoutError
— время ожидания истекло;
12 ReferenceError
— попытка доступа к объекту с помощью слабой ссылки, когда объект не существует;
13 RuntimeError
— генерируется в случае, когда исключение не может быть классифицировано или не подпадает под любую другую категорию;
14 NotImplementedError
— абстрактные методы класса нуждаются в переопределении;
15 SyntaxError
— ошибка синтаксиса;
16 SystemError
— сигнализирует о внутренне ошибке;
17 TypeError
— операция не может быть выполнена с переменной этого типа;
18 ValueError
— возникает когда в функцию передается объект правильного типа, но имеющий некорректное значение;
19 UnicodeError
— исключение связанное с кодирование текста в unicode
, бывает трех видов:
UnicodeEncodeError
— ошибка кодирования;UnicodeDecodeError
— ошибка декодирования;UnicodeTranslateError
— ошибка переводаunicode
.
20 Warning
— предупреждение, некритическая ошибка.
💭 Посмотреть всю цепочку наследования конкретного типа исключения можно с помощью модуля inspect
:
import inspect
print(inspect.getmro(TimeoutError))
> (<class 'TimeoutError'>, <class 'OSError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)
📄 Подробное описание всех классов встроенных исключений в Python смотрите в официальной документации.
Как создать свой тип Exception
В Python можно создавать свои исключения. При этом есть одно обязательное условие: они должны быть потомками класса Exception
:
class MyError(Exception):
def __init__(self, text):
self.txt = text
try:
raise MyError('Моя ошибка')
except MyError as er:
print(er)
> Моя ошибка
С помощью try/except
контролируются и обрабатываются ошибки в приложении. Это особенно актуально для критически важных частей программы, где любые «падения» недопустимы (или могут привести к негативным последствиям). Например, если программа работает как «демон», падение приведет к полной остановке её работы. Или, например, при временном сбое соединения с базой данных, программа также прервёт своё выполнение (хотя можно было отловить ошибку и попробовать соединиться в БД заново).
Вместе с try/except
можно использовать дополнительные блоки. Если использовать все блоки описанные в статье, то код будет выглядеть так:
try:
# попробуем что-то сделать
except (ZeroDivisionError, ValueError) as e:
# обрабатываем исключения типа ZeroDivisionError или ValueError
except Exception as e:
# исключение не ZeroDivisionError и не ValueError
# поэтому обрабатываем исключение общего типа (унаследованное от Exception)
# сюда не сходят исключения типа GeneratorExit, KeyboardInterrupt, SystemExit
else:
# этот блок выполняется, если нет исключений
# если в этом блоке сделать return, он не будет вызван, пока не выполнился блок finally
finally:
# этот блок выполняется всегда, даже если нет исключений else будет проигнорирован
# если в этом блоке сделать return, то return в блоке
Подробнее о работе с исключениями в Python можно ознакомиться в официальной документации.
Уровень сложности
Средний
Время на прочтение
8 мин
Количество просмотров 6K
Люди, которые пишут код, часто воспринимают работу с исключениями как необходимое зло. Но освоение системы обработки исключений в Python способно повысить профессиональный уровень программиста, сделать его эффективнее. В этом материале я разберу следующие темы, изучение которых поможет всем желающим раскрыть потенциал Python через разумный подход к обработке исключений:
-
Что такое обработка исключений?
-
Разница между оператором
if
и обработкой исключений. -
Использование разделов
else
иfinally
блокаtry-except
для организации правильного обращения с ошибками. -
Определение пользовательских исключений.
-
Рекомендации по обработке исключений.
Что такое обработка исключений?
Обработка исключений — это процесс написания кода для перехвата и обработки ошибок или исключений, которые могут возникать при выполнении программы. Это позволяет разработчикам создавать надёжные программы, которые продолжают работать даже при возникновении неожиданных событий или ошибок. Без системы обработки исключений подобное обычно приводит к фатальным сбоям.
Когда возникают исключения — Python выполняет поиск подходящего обработчика исключений. После этого, если обработчик будет найден, выполняется его код, в котором предпринимаются уместные действия. Это может быть логирование данных, вывод сообщения, попытка восстановить работу программы после возникновения ошибки. В целом можно сказать, что обработка исключения помогает повысить надёжность Python-приложений, улучшает возможности по их поддержке, облегчает их отладку.
Различия между оператором if и обработкой исключений
Главные различия между оператором if
и обработкой исключений в Python произрастают из их целей и сценариев использования.
Оператор if
— это базовый строительный элемент структурного программирования. Этот оператор проверяет условие и выполняет различные блоки кода, основываясь на том, истинно проверяемое условие или ложно. Вот пример:
temperature = int(input("Please enter temperature in Fahrenheit: "))
if temperature > 100:
print("Hot weather alert! Temperature exceeded 100°F.")
elif temperature >= 70:
print("Warm day ahead, enjoy sunny skies.")
else:
print("Bundle up for chilly temperatures.")
Обработка исключений, с другой стороны, играет важную роль в написании надёжных и отказоустойчивых программ. Эта роль раскрывается через работу с неожиданными событиями и ошибками, которые могут возникать во время выполнения программы.
Исключения используются для подачи сигналов о проблемах и для выявления участков кода, которые нуждаются в улучшении, отладке, или в оснащении их дополнительными механизмами для проверки ошибок. Исключения позволяют Python достойно справляться с ситуациями, в которых возникают ошибки. В таких ситуациях исключения дают возможность продолжать выполнение скрипта вместо того, чтобы резко его останавливать.
Рассмотрим следующий код, демонстрирующий пример того, как можно реализовать обработку исключений и улучшить ситуацию с потенциальными отказами, связанными с делением на ноль:
# Определение функции, которая пытается поделить число на ноль
def divide(x, y):
result = x / y
return result
# Вызов функции divide с передачей ей x=5 и y=0
result = divide(5, 0)
print(f"Result of dividing {x} by {y}: {result}")
Вывод:
Traceback (most recent call last):
File "<stdin>", line 8, in <module>
ZeroDivisionError: division by zero attempted
После того, как было сгенерировано исключение, программа, не дойдя до инструкции print
, сразу же прекращает выполняться.
Вышеописанное исключение можно обработать, обернув вызов функции divide
в блок try-except
:
# Определение функции, которая пытается поделить число на ноль
def divide(x, y):
result = x / y
return result
# Вызов функции divide с передачей ей x=5 и y=0
try:
result = divide(5, 0)
print(f"Result of dividing {x} by {y}: {result}")
except ZeroDivisionError:
print("Cannot divide by zero.")
Вывод:
Cannot divide by zero.
Сделав это, мы аккуратно обработали исключение ZeroDivisionError
, предотвратили аварийное завершение остального кода из-за необработанного исключения.
Подробности о других встроенных Python-исключениях можно найти здесь.
Использование разделов else и finally блока try-except для организации правильного обращения с ошибками
При работе с исключениями в Python рекомендуется включать в состав блоков try-except
и раздел else
, и раздел finally
. Раздел else
позволяет программисту настроить действия, производимые в том случае, если при выполнении кода, который защищают от проблем, не было вызвано исключений. А раздел finally
позволяет обеспечить обязательное выполнение неких заключительных операций, вроде освобождения ресурсов, независимо от факта возникновения исключений (вот и вот — полезные материалы об этом).
Например — рассмотрим ситуацию, когда нужно прочитать данные из файла и выполнить какие-то действия с этими данными. Если при чтении файла возникнет исключение — программист может решить, что надо залогировать ошибку и остановить выполнение дальнейших операций. Но в любом случае файл нужно правильно закрыть.
Использование разделов else
и finally
позволяет поступить именно так — обработать данные обычным образом в том случае, если исключений не возникло, либо обработать любые исключения, но, как бы ни развивались события, в итоге закрыть файл. Без этих разделов код страдал бы уязвимостями в виде утечки ресурсов или неполной обработки ошибок. В результате оказывается, что else
и finally
играют важнейшую роль в создании устойчивых к ошибкам и надёжных программ.
try:
# Открытие файла в режиме чтения
file = open("file.txt", "r")
print("Successful opened the file")
except FileNotFoundError:
# Обработка ошибки, возникающей в том случае, если файл не найден
print("File Not Found Error: No such file or directory")
exit()
except PermissionError:
# Обработка ошибок, связанных с разрешением на доступ к файлу
print("Permission Denied Error: Access is denied")
else:
# Всё хорошо - сделать что-то с данными, прочитанными из файла
content = file.read().decode('utf-8')
processed_data = process_content(content)
# Прибираемся после себя даже в том случае, если выше возникло исключение
finally:
file.close()
В этом примере мы сначала пытаемся открыть файл file.txt
для чтения (в подобной ситуации можно использовать выражение with
, которое гарантирует правильное автоматическое закрытие объекта файла после завершения работы). Если в процессе выполнения операций файлового ввода/вывода возникают ошибки FileNotFoundError
или PermissionError
— выполняются соответствующие разделы except
. Здесь, ради простоты, мы лишь выводим на экран сообщения об ошибках и выходим из программы в том случае, если файл не найден.
В противном случае, если в блоке try
исключений не возникло, мы продолжаем работу, обрабатывая содержимое файла в ветви else
. И наконец — выполняется «уборка» — файл закрывается независимо от возникновения исключения. Это обеспечивает блок finally
(подробности смотрите здесь).
Применяя структурированный подход к обработке исключений, напоминающий вышеописанный, можно поддерживать свой код в хорошо организованном состоянии и обеспечивать его читабельность. При этом код будет рассчитан на борьбу с потенциальными ошибками, которые могут возникнуть при взаимодействии с внешними системами или входными данными.
Определение пользовательских исключений
В Python можно определять пользовательские исключения путём создания подклассов встроенного класса Exception
или любых других классов, являющихся прямыми наследниками Exception
.
Для того чтобы определить собственное исключение — нужно создать новый класс, являющийся наследником одного из подходящих классов, и оснастить этот класс атрибутами, соответствующими нуждам программиста. Затем новый класс можно использовать в собственном коде, работая с ним так же, как работают со встроенными классами исключений.
Вот пример определения пользовательского исключения, названного InvalidEmailAddress
:
class InvalidEmailAddress(ValueError):
def __init__(self, message):
super().__init__(message)
self.msgfmt = message
Это исключение является наследником ValueError
. Его конструктор принимает необязательный аргумент message
(по умолчанию он устанавливается в значение invalid email address
).
Вызвать это исключение можно в том случае, если в программе встретился адрес электронной почты, имеющий некорректный формат:
def send_email(address):
if isinstance(address, str) == False:
raise InvalidEmailAddress("Invalid email address")
# Отправка электронного письма
Теперь, если функции send_email()
будет передана строка, содержащая неправильно оформленный адрес, то, вместо сообщения стандартной ошибки TypeError
, будет выдано настроенное заранее сообщение об ошибке, которое чётко указывает на возникшую проблему. Например, это может выглядеть так:
>>> send_email(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/path/to/project/main.py", line 8, in send_email
raise InvalidEmailAddress("Invalid email address")
InvalidEmailAddress: Invalid email address
Рекомендации по обработке исключений
Вот несколько рекомендаций, относящихся к обработке ошибок в Python:
-
Проектируйте код в расчёте на возможное возникновение ошибок. Заранее планируйте устройство кода с учётом возможных сбоев и проектируйте программы так, чтобы они могли бы достойно обрабатывать эти сбои. Это означает — предугадывать возможные пограничные случаи и реализовывать подходящие обработчики ошибок.
-
Используйте содержательные сообщения об ошибках. Сделайте так, чтобы программа выводила бы, на экран, или в файл журнала, подробные сообщения об ошибках, которые помогут пользователям понять — что и почему пошло не так. Старайтесь не применять обобщённые сообщения об ошибках, наподобие
Error occurred
илиSomething bad happened
. Вместо этого подумайте об удобстве пользователя и покажите сообщение, в котором будет дан совет по решению проблемы или будет приведена ссылка на документацию. Постарайтесь соблюсти баланс между выводом подробных сообщений и перегрузкой пользовательского интерфейса избыточными данными. -
Минимизируйте побочные эффекты. Постарайтесь свести к минимуму последствия сбойных операций, изолируя проблемные разделы кода посредством конструкции
try-finally
илиtry
с использованиемwith
. Сделайте так, чтобы после выполнения кода, было ли оно удачным или нет, обязательно выполнялись бы «очистительные» операции. -
Тщательно тестируйте код. Обеспечьте корректное поведение обработчиков ошибок в различных сценариях использования программы, подвергнув код всеобъемлющему тестированию.
-
Регулярно выполняйте рефакторинг кода. Выполняйте рефакторинг фрагментов кода, подверженных ошибкам, чтобы улучшить их надёжность и производительность. Постарайтесь, чтобы ваша кодовая база была бы устроена по модульному принципу, чтобы её отдельные части слабо зависели бы друг от друга. Это позволяет независимым частям код самостоятельно эволюционировать, не оказывая негативного воздействия на другие его части.
-
Логируйте важные события. Следите за интересными событиями своего приложения, записывая сведения о них в файл журнала или выводя в консоль. Это поможет вам выявлять проблемы на ранних стадиях их возникновения, не тратя время на длительный анализ большого количества неструктурированных логов.
Итоги
Написание кода обработки ошибок — это неотъемлемая часть индустрии разработки ПО, и, в частности — разработки на Python. Это позволяет разработчикам создавать более надёжные и стабильные программы. Следуя индустриальным стандартам и рекомендациям по обработке исключений, разработчик может сократить время, необходимое на отладку кода, способен обеспечить написание качественных программ и сделать так, чтобы пользователям было бы приятно работать с этими программами.
О, а приходите к нам работать? 🤗 💰
Мы в wunderfund.io занимаемся высокочастотной алготорговлей с 2014 года. Высокочастотная торговля — это непрерывное соревнование лучших программистов и математиков всего мира. Присоединившись к нам, вы станете частью этой увлекательной схватки.
Мы предлагаем интересные и сложные задачи по анализу данных и low latency разработке для увлеченных исследователей и программистов. Гибкий график и никакой бюрократии, решения быстро принимаются и воплощаются в жизнь.
Сейчас мы ищем плюсовиков, питонистов, дата-инженеров и мл-рисерчеров.
Присоединяйтесь к нашей команде.
In this Python Tutorial let us learn about the 3 different pieces of information that you can extract and use from the Exceptions caught on your except clauses, and see the best ways to use each of these pieces in our Python programs.
Let us start by learning what the 3 pieces of information are.
What kind of information can you get from Exceptions?
You can get the following 3 pieces of data from exceptions
- Exception Type,
- Exception Value a.k.a Error Message, and
- Stack-trace or Traceback Object.
All three of the above information is printed out by the Python Interpreter when our program crashes due to an exception as shown in the following example
>> my_list = [1,2]
>> print (my_list[3])
Traceback (most recent call last):
File "<ipython-input-35-63c7f9106be5>", line 1, in <module>
print (my_list[3])
IndexError: list index out of range
Lines 3,4,5,6 shows the Stack-trace
Line 7 shows the Exception type and Error Message.
Our focus in this article is to learn how to extract the above 3 pieces individually in our except clauses and print them out as needed.
Hence, the rest of the article is all about answering the following questions
- what does each of the information in the above list mean,
- how to extract each of these 3 pieces individually and
- how to use these pieces in our programs.
Piece#1: Printing Exception Type
The Exception Type refers to the class to which the Exception that you have just caught belongs to.
Extracting Piece#1 (Exception Type)
Let us improve our Example 1 above by putting the problematic code into try and except clauses.
try:
my_list = [1,2]
print (my_list[3])
except Exception as e:
print(type(e))
Here, in the try clause, we have declared a List named my_list and initialized the list with 2 items. Then we have tried to print the 3rd/non-existent item in the list.
The except clause catches the IndexError exception and prints out Exception type.
On running the code, we will get the following output
<class 'IndexError'>
As you can see we just extracted and printed out the information about the class to which the exception that we have just caught belongs to!
But how exactly did we do that?
If you have a look at the except clause. In the line
except Exception as e:
what we have done is, we have assigned the caught exception to an object named “e”. Then by using the built-in python function type(), we have printed out the class name that the object e belongs to.
print(type(e))
Where to get more details about Exception Types
Now that we have the “Exception Type”, next we will probably need to get some information about that particular type so that we can get a better understanding of why our code has failed. In order to do that, the best place to start is the official documentation.
For built in exceptions you can have a look at the Python Documentation
For Exception types that come with the libraries that you use with your code, refer to the documentation of your library.
Piece#2: Printing Exception Value a.k.a Error message
The Exception type is definitely useful for debugging, but, a message like IndexError might sound cryptic and a good understandable error-message will make our job of debugging easier without having to look at the documentation.
In other words, if your program is to be run on the command line and you wish to log why the program just crashed then it is better to use an “Error message” rather than an “Exception Type”.
The example below shows how to print such an Error message.
try:
my_list = [1,2]
print (my_list[3])
except Exception as e:
print(e)
This will print the default error message as follows
list index out of range
Each Exception type comes with its own error message. This can be retrieved using the built-in function print().
Say your program is going to be run by a not-so-tech-savvy user, in that case, you might want to print something friendlier. You can do so by passing in the string to be printed along with the constructor as follows.
try:
raise IndexError('Custom message about IndexError')
except Exception as e:
print(e)
This will print
Custom message about IndexError
To understand how the built-in function print() does this magic, and see some more examples of manipulating these error messages, I recommend reading my other article in the link below.
Python Exception Tutorial: Printing Error Messages (5 Examples!)
If you wish to print both the Error message and the Exception type, which I recommend, you can do so like below.
try:
my_list = [1,2]
print (my_list[3])
except Exception as e:
print(repr(e))
This will print something like
IndexError('list index out of range')
Now that we have understood how to get control over the usage of Pieces 1 and 2, let us go ahead and look at the last and most important piece for debugging, the stack-trace which tells us where exactly in our program have the Exception occurred.
Piece#3: Printing/Logging the stack-trace using the traceback object
Stack-trace in Python is packed into an object named traceback object.
This is an interesting one as the traceback class in Python comes with several useful methods to exercise complete control over what is printed.
Let us see how to use these options using some examples!
import traceback
try:
my_list = [1,2]
print (my_list[3])
except Exception:
traceback.print_exc()
This will print something like
Traceback (most recent call last):
File "<ipython-input-38-f9a1ee2cf77a>", line 5, in <module>
print (my_list[3])
IndexError: list index out of range
which contains the entire error messages printed by the Python interpreter if we fail to handle the exception.
Here, instead of crashing the program, we have printed this entire message using our exception handler with the help of the print_exc() method of the traceback class.
The above Example-6 is too simple, as, in the real-world, we will normally have several nested function calls which will result in a deeper stack. Let us see an example of how to control the output in such situations.
def func3():
my_list = [1,2]
print (my_list[3])
def func2():
print('calling func3')
func3()
def func1():
print('calling func2')
func2()
try:
print('calling func1')
func1()
except Exception as e:
traceback.print_exc()
Here in the try clause we call func1(), which in-turn calls func2(), which in-turn calls func3(), which produces an IndexError. Running the code above we get the following output
calling func1
calling func2
calling func3
Traceback (most recent call last):
File "<ipython-input-42-2267707e164f>", line 15, in <module>
func1()
File "<ipython-input-42-2267707e164f>", line 11, in func1
func2()
File "<ipython-input-42-2267707e164f>", line 7, in func2
func3()
File "<ipython-input-42-2267707e164f>", line 3, in func3
print (my_list[3])
IndexError: list index out of range
Say we are not interested in some of the above information. Say we just want to print out the Traceback and skip the error message and Exception type (the last line above), then we can modify the code like shown below.
def func3():
my_list = [1,2]
print (my_list[3])
def func2():
func3()
def func1():
func2()
try:
func1()
except Exception as e:
traceback_lines = traceback.format_exc().splitlines()
for line in traceback_lines:
if line != traceback_lines[-1]:
print(line)
Here we have used the format_exc() method available in the traceback class to get the traceback information as a string and used splitlines() method to transform the string into a list of lines and stored that in a list object named traceback_lines
Then with the help of a simple for loop we have skipped printing the last line with index of -1 to get an output like below
Traceback (most recent call last):
File "<ipython-input-43-aff649563444>", line 3, in <module>
func1()
File "<ipython-input-42-2267707e164f>", line 11, in func1
func2()
File "<ipython-input-42-2267707e164f>", line 7, in func2
func3()
File "<ipython-input-42-2267707e164f>", line 3, in func3
print (my_list[3])
Another interesting variant of formatting the information in the traceback object is to control the depth of stack that is actually printed out.
If your program uses lots of external library code, odds are the stack will get very deep, and printing out each and every level of function call might not be very useful. If you ever find yourself in such a situation you can set the limit argument in the print_exc() method like shown below.
traceback.print_exc(limit=2, file=sys.stdout)
This will limit the number of levels to 2. Let us use this line of code in our Example and see how that behaves
def func3():
my_list = [1,2]
print (my_list[3])
def func2():
func3()
def func1():
func2()
try:
func1()
except Exception as e:
traceback.print_exc(limit=2)
This will print
Traceback (most recent call last):
File "<ipython-input-44-496132ff4faa>", line 12, in <module>
func1()
File "<ipython-input-44-496132ff4faa>", line 9, in func1
func2()
IndexError: list index out of range
As you can see, we have printed only 2 levels of the stack and skipped the 3rd one, just as we wanted!
You can do more things with traceback like formatting the output to suit your needs. If you are interested to learn even more things to do, refer to the official documentation on traceback here
Now that we have seen how to exercise control over what gets printed and how to format them, next let us have a look at some best practices on when to use which piece of information
Best Practices while Printing Exception messages
When to Use Which Piece
- Use Piece#1 only on very short programs and only during the development/testing phase to get some clues on the Exceptions without letting the interpreter crash your program. Once finding out, implement specific handlers to do something about these exceptions. If you are not sure how to handle the exceptions have a look at my other article below where I have explained 3 ways to handle Exceptions
Exceptions in Python: Everything You Need To Know! - Use Piece#2 to print out some friendly information either for yourself or for your user to inform them what exactly is happening.
- Use all 3 pieces on your finished product, so that if an exception ever occurs while your program is running on your client’s computer, you can log the errors and have use that information to fix your bugs.
Where to print
One point worth noting here is that the default file that print() uses is the stdout file stream and not the stderr stream. To use stderr instead, you can modify the code like this
import sys
try:
#some naughty statements that irritate us with exceptions
except Exception as e:
print(e, file=sys.stderr)
The above code is considered better practice, as errors are meant to go to stderr and not stdout.
You can always print these into a separate log file too if that is what you need. This way, you can organize the logs collected in a better manner by separating the informational printouts from the error printouts.
How to print into log files
If you are going to use a log file I suggest using python’s logging module instead of print() statements, as described here
If you are interested in learning how to manually raise exceptions, and what situations you might need to do that you can read this article below
Python: Manually throw/raise an Exception using the “raise” statement
If you are interested in catching and handling multiple exception in a single except clause, you can this article below
Python: 3 Ways to Catch Multiple Exceptions in a single “except” clause
And with that, I will conclude this article!
I hope you enjoyed reading this article and got some value out of it!
Feel free to share it with your friends and colleagues!
Введение | |
Пример с базовым Exception | |
Два исключения | |
except Error as e:: Печать текста ошибки | |
else | |
finally | |
raise | |
Пример 2 | |
Пример 3 | |
Исключения, которые не нужно обрабатывать | |
Список исключений | |
Разбор примеров: IndexError, ValueError, KeyError | |
Похожие статьи |
Введение
Если в коде есть ошибка, которую видит интерпретатор поднимается исключение, создается так называемый
Exception Object, выполнение останавливается, в терминале
показывается Traceback.
В английском языке используется словосочетание Raise Exception
Исключение, которое не было предусмотрено разработчиком называется необработанным (Unhandled Exception)
Такое поведение не всегда является оптимальным. Не все ошибки дожны останавливать работу кода.
Возможно, где-то разработчик ожидает появление ошибок и их можно обработать по-другому.
try и except нужны прежде всего для того, чтобы код правильно реагировал на возможные ошибки и продолжал выполняться
там, где появление ошибки некритично.
Исключение, которое предусмотрено в коде называется обработанным (Handled)
Блок try except имеет следующий синтаксис
try:
pass
except Exception:
pass
else:
pass
finally:
pass
В этой статье я создал файл
try_except.py
куда копирую код из примеров.
Пример
Попробуем открыть несуществующий файл и воспользоваться базовым Exception
try:
f = open(‘missing.txt’)
except Exception:
print(‘ERR: File not found’)
python try_except.py
ERR: No missing.txt file found
Ошибка поймана, видно наше сообщение а не Traceback
Проверим, что когда файл существует всё хорошо
try:
f = open(‘existing.txt’)
except Exception:
print(‘ERR: File not found’)
python try_except.py
Пустота означает успех
Два исключения
Если ошибок больше одной нужны дополнительные исключения. Попробуем открыть существующий файл, и после этого
добавить ошибку.
try:
f = open(‘existing.txt’)
x = bad_value
except Exception:
print(‘ERR: File not found’)
python try_except.py
ERR: File not found
Файл открылся, но так как в следующей строке ошибка — в терминале появилось вводящее в заблуждение сообщение.
Проблема не в том, что «File not found» а в том, что bad_value нигде не определёно.
Избежать сбивающих с толку сообщений можно указав тип ожидаемой ошибки. В данном примере это FileNotFoundError
try:
# expected exception
f = open(‘existing.txt’)
# unexpected exception should result in Traceback
x = bad_value
except FileNotFoundError:
print(‘ERR: File not found’)
python try_except.py
Traceback (most recent call last):
File «/home/andrei/python/try_except2.py», line 5, in <module>
x = bad_value
NameError: name ‘bad_value’ is not defined
Вторая ошибка не поймана поэтому показан Traceback
Поймать обе ошибки можно добавив второй Exception
try:
# expected exception should be caught by FileNotFoundError
f = open(‘missing.txt’)
# unexpected exception should be caught by Exception
x = bad_value
except FileNotFoundError:
print(‘ERR: File not found’)
except Exception:
print(‘ERR: Something unexpected went wrong’)
python try_except.py
ERR: File not found
ERR: Something unexpected went wrong
Печать текста ошибки
Вместо своего текста можно выводить текст ошибки. Попробуем с существующим файлом — должна быть одна пойманная ошибка.
try:
# expected exception should be caught by FileNotFoundError
f = open(‘existing.txt’)
# unexpected exception should be caught by Exception
x = bad_value
except FileNotFoundError as e:
print(e)
except Exception as e:
print(e)
python try_except.py
name ‘bad_value’ is not defined
Теперь попытаемся открыть несуществующий файл — должно быть две пойманные ошибки.
try:
# expected exception should be caught by FileNotFoundError
f = open(‘missing.txt’)
# unexpected exception should be caught by Exception
x = bad_value
except FileNotFoundError as e:
print(e)
except Exception as e:
print(e)
python try_except.py
name ‘bad_value’ is not defined
[Errno 2] No such file or directory: ‘missing.txt’
else
Блок else будет выполнен если исключений не будет поймано.
Попробуем открыть существующий файл
existing.txt
в котором есть строка
www.heihei.ru
try:
f = open(‘existing.txt’)
except FileNotFoundError as e:
print(e)
except Exception as e:
print(e)
else:
print(f.read())
f.close()
python try_except.py
www.heihei.ru
Если попробовать открыть несуществующий файл
missing.txt
то исключение обрабатывается, а код из блока else не выполняется.
[Errno 2] No such file or directory: ‘missing.txt’
finally
Блок finally будет выполнен независимо от того, поймано исключение или нет
try:
f = open(‘existing.txt’)
except FileNotFoundError as e:
print(e)
except Exception as e:
print(e)
else:
print(f.read())
f.close()
finally:
print(«Finally!»)
www.heihei.ru
Finally!
А если попытаться открыть несуществующий
missing.txt
[Errno 2] No such file or directory: ‘missing.txt’
Finally!
Когда нужно применять finally:
Рассмотрим скрипт, который вносит какие-то изменения в систему.
Затем он пытается что-то сделать. В конце возвращает
систему в исходное состояние.
Если ошибка случится в середине скрипта — он уже не сможет вернуть систему в исходное состояние.
Но если вынести возврат к исходному состоянию в блок finally он сработает даже при ошибке
в предыдущем блоке.
import os
def make_at(path, dir_name):
original_path = os.getcwd()
os.chdir(path)
os.mkdir(dir_name)
os.chdir(original_path)
Этот скрипт не вернётся в исходную директорию при ошибке в os.mkdir(dir_name)
А у скрипта ниже такой проблемы нет
def make_at(path, dir_name):
original_path = os.getcwd()
os.chdir(path)
try:
os.mkdir(dir_name)
finally:
os.chdir(original_path)
Не лишнима будет добавить обработку и вывод исключения
import os
import sys
def make_at(path, dir_name):
original_path = os.getcwd()
os.chdir(path)
try:
os.mkdir(dir_name)
except OSError as e:
print(e, file=sys.stderr)
raise
finally:
os.chdir(original_path)
По умолчанию print() выводит в sys.stdout, но в случае ислючений логичнее выводить в sys.stderr
raise
Можно вызывать исключения вручную в любом месте кода с помощью
raise.
try:
f = open(‘outdated.txt’)
if f.name == ‘outdated.txt’:
raise Exception
except FileNotFoundError as e:
print(e)
except Exception as e:
print(‘File is outdated!’)
else:
print(f.read())
f.close()
finally:
print(«Finally!»)
python try_except.py
File is outdated!
Finally!
raise
можно использовать для перевызова исключения, например, чтобы уйти от использования кодов ошибок.
Для этого достаточно вызвать raise без аргументов — поднимется текущее исключение.
Пример 2
Рассмотрим функцию, которая принимает числа прописью и возвращает цифрами
DIGIT_MAP = {
‘zero’: ‘0’,
‘one’: ‘1’,
‘two’: ‘2’,
‘three’: ‘3’,
‘four’: ‘4’,
‘five’: ‘5’,
‘six’: ‘6’,
‘seven’: ‘7’,
‘eight’: ‘8’,
‘nine’: ‘9’,
}
def convert(s):
number = »
for token in s:
number += DIGIT_MAP[token]
x = int(number)
return x
python
>>> from exc1 import convert
>>> convert(«one three three seven».split())
1337
Теперь передадим аргумент, который не предусмотрен в словаре
>>> convert(«something unseen«.split())
Traceback (most recent call last):
File «<stdin>», line 1, in <module>
File «/home/andrei/python/exc1.py», line 17, in convert
number &plu= DIGIT_MAP[token]
KeyError: ‘something’
KeyError — это тип Exception объекта. Полный список можно изучить в конце статьи.
Исключение прошло следующий путь:
REPL → convert() → DIGIT_MAP(«something») → KeyError
Обработать это исключение можно внеся изменения в функцию convert
convert(s):
try:
number = »
for token in s:
number += DIGIT_MAP[token]
x = int(number)
print(«Conversion succeeded! x = «, x)
except KeyError:
print(«Conversion failed!»)
x = —1
return x
>>> from exc1 import convert
>>> convert(«one nine six one».split())
Conversion succeeded! x = 1961
1961
>>> convert(«something unseen».split())
Conversion failed!
-1
Эта обработка не спасает если передать int вместо итерируемого объекта
>>> convert(2022)
Traceback (most recent call last):
File «<stdin>», line 1, in <module>
File «/home/andrei/python/exc1.py», line 17, in convert
for token in s:
TypeError: ‘int’ object is not iterable
Нужно добавить обработку TypeError
…
except KeyError:
print(«Conversion failed!»)
x = —1
except TypeError:
print(«Conversion failed!»)
x = —1
return x
>>> from exc1 import convert
>>> convert(«2022».split())
Conversion failed!
-1
Избавимся от повторов, удалив принты, объединив два исключения в кортеж и вынесем присваивание значения x
из try блока.
Также добавим
докстринг
с описанием функции.
def convert(s):
«»»Convert a string to an integer.»»»
x = —1
try:
number = »
for token in s:
number += DIGIT_MAP[token]
x = int(number)
except (KeyError, TypeError):
pass
return x
>>> from exc4 import convert
>>> convert(«one nine six one».split())
1961
>>> convert(«bad nine six one».split())
-1
>>> convert(2022)
-1
Ошибки обрабатываются, но без принтов, процесс не очень информативен.
Грамотно показать текст сообщений об ошибках можно импортировав sys и изменив функцию
import sys
DIGIT_MAP = {
‘zero’: ‘0’,
‘one’: ‘1’,
‘two’: ‘2’,
‘three’: ‘3’,
‘four’: ‘4’,
‘five’: ‘5’,
‘six’: ‘6’,
‘seven’: ‘7’,
‘eight’: ‘8’,
‘nine’: ‘9’,
}
def convert(s):
«»»Convert a string to an integer.»»»
try:
number = »
for token in s:
number += DIGIT_MAP[token]
return(int(number))
except (KeyError, TypeError) as e:
print(f«Conversion error: {e!r}», file=sys.stderr)
return —1
>>> from exc1 import convert
>>> convert(2022)
Conversion error: TypeError(«‘int’ object is not iterable»)
-1
>>> convert(«one nine six one».split())
1961
>>> convert(«bad nine six one».split())
Conversion error: KeyError(‘bad’)
Ошибки обрабатываются и их текст виден в терминале.
С помощью
!r
выводится
repr()
ошибки
raise вместо кода ошибки
В предыдущем примере мы полагались на возвращение числа -1 в качестве кода ошибки.
Добавим к коду примера функцию string_log() и поработаем с ней
def string_log(s):
v = convert(s)
return log(v)
>>> from exc1 import string_log
>>> string_log(«one two eight».split())
4.852030263919617
>>> string_log(«bad one two».split())
Conversion error: KeyError(‘bad’)
Traceback (most recent call last):
File «<stdin>», line 1, in <module>
File «/home/andrei/exc1.py», line 32, in string_log
return log(v)
ValueError: math domain error
convert() вернул -1 а string_log попробовал его обработать и не смог.
Можно заменить return -1 на raise. Это считается более правильным подходом в Python
def convert(s):
«»»Convert a string to an integer.»»»
try:
number = »
for token in s:
number += DIGIT_MAP[token]
return(int(number))
except (KeyError, TypeError) as e:
print(f«Conversion error: {e!r}», file=sys.stderr)
raise
>>> from exc7 import string_log
>>> string_log(«one zero».split())
2.302585092994046
>>> string_log(«bad one two».split())
Conversion error: KeyError(‘bad’)
Traceback (most recent call last):
File «<stdin>», line 1, in <module>
File «/home/andrei/exc7.py», line 31, in string_log
v = convert(s)
File «/home/andrei/exc7.py», line 23, in convert
number += DIGIT_MAP[token]
KeyError: ‘bad’
Пример 3
Рассмотрим алгоритм по поиску квадратного корня
def sqrt(x):
«»»Compute square roots using the method
of Heron of Alexandria.
Args:
x: The number for which the square root
is to be computed.
Returns:
The square root of x.
«»»
guess = x
i = 0
while guess * guess != x and i < 20:
guess = (guess + x / guess) / 2.0
i += 1
return guess
def main():
print(sqrt(9))
print(sqrt(2))
if __name__ == ‘__main__’:
main()
python sqrt_ex.py
3.0
1.414213562373095
При попытке вычислить корень от -1 получим ошибку
def main():
print(sqrt(9))
print(sqrt(2))
print(sqrt(-1))
python sqrt_ex.py
3.0
1.414213562373095
Traceback (most recent call last):
File «/home/andrei/sqrt_ex.py», line 26, in <module>
main()
File «/home/andrei/sqrt_ex.py», line 23, in main
print(sqrt(-1))
File «/home/andrei/sqrt_ex.py», line 16, in sqrt
guess = (guess + x / guess) / 2.0
ZeroDivisionError: float division by zero
В строке
guess = (guess + x / guess) / 2.0
Происходит деление на ноль
Обработать можно следующим образом:
def main():
try:
print(sqrt(9))
print(sqrt(2))
print(sqrt(-1))
except ZeroDivisionError:
print(«Cannot compute square root «
«of a negative number.»)
print(«Program execution continues «
«normally here.»)
Обратите внимание на то, что в try помещены все вызовы функции
python sqrt_ex.py
3.0
1.414213562373095
Cannot compute square root of a negative number.
Program execution continues normally here.
Если пытаться делить на ноль несколько раз — поднимется одно исключение и всё что находится в блоке
try после выполняться не будет
def main():
try:
print(sqrt(9))
print(sqrt(-1))
print(sqrt(2))
print(sqrt(-1))
python sqrt_ex.py
3.0
Cannot compute square root of a negative number.
Program execution continues normally here.
Каждую попытку вычислить корень из -1 придётся обрабатывать отдельно. Это кажется неудобным, но
в этом и заключается смысл — каждое место где вы ждёте ислючение нужно помещать в свой
try except блок.
Можно обработать исключение так:
try:
while guess * guess != x and i < 20:
guess = (guess + x / guess) / 2.0
i += 1
except ZeroDivisionError:
raise ValueError()
return guess
def main():
print(sqrt(9))
print(sqrt(-1))
python sqrt_ex.py
3.0
Traceback (most recent call last):
File «/home/andrei/sqrt_ex3.py», line 17, in sqrt
guess = (guess + x / guess) / 2.0
ZeroDivisionError: float division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File «/home/andrei/sqrt_ex3.py», line 30, in <module>
main()
File «/home/andrei/sqrt_ex3.py», line 25, in main
print(sqrt(-1))
File «/home/andrei/sqrt_ex3.py», line 20, in sqrt
raise ValueError()
ValueError
Гораздо логичнее поднимать исключение сразу при получении аргумента
def sqrt(x):
«»»Compute square roots using the method
of Heron of Alexandria.
Args:
x: The number for which the square root
is to be computed.
Returns:
The square root of x.
Raises:
ValueError: If x is negative
«»»
if x < 0:
raise ValueError(
«Cannot compute square root of «
f«negative number {x}»)
guess = x
i = 0
while guess * guess != x and i < 20:
guess = (guess + x / guess) / 2.0
i += 1
return guess
def main():
print(sqrt(9))
print(sqrt(-1))
print(sqrt(2))
print(sqrt(-1))
if __name__ == ‘__main__’:
main()
python sqrt_ex.py
3.0
Traceback (most recent call last):
File «/home/avorotyn/python/lessons/pluralsight/core_python_getting_started/chapter8/sqrt_ex4.py», line 35, in <module>
main()
File «/home/avorotyn/python/lessons/pluralsight/core_python_getting_started/chapter8/sqrt_ex4.py», line 30, in main
print(sqrt(-1))
File «/home/avorotyn/python/lessons/pluralsight/core_python_getting_started/chapter8/sqrt_ex4.py», line 17, in sqrt
raise ValueError(
ValueError: Cannot compute square root of negative number -1
Пока получилось не очень — виден Traceback
Убрать Traceback можно добавив обработку ValueError в вызов функций
import sys
def sqrt(x):
«»»Compute square roots using the method
of Heron of Alexandria.
Args:
x: The number for which the square root
is to be computed.
Returns:
The square root of x.
Raises:
ValueError: If x is negative
«»»
if x < 0:
raise ValueError(
«Cannot compute square root of «
f«negative number {x}»)
guess = x
i = 0
while guess * guess != x and i < 20:
guess = (guess + x / guess) / 2.0
i += 1
return guess
def main():
try:
print(sqrt(9))
print(sqrt(2))
print(sqrt(-1))
print(«This is never printed»)
except ValueError as e:
print(e, file=sys.stderr)
print(«Program execution continues normally here.»)
if __name__ == ‘__main__’:
main()
python sqrt_ex.py
3.0
1.414213562373095
Cannot compute square root of negative number -1
Program execution continues normally here.
Исключения, которые не нужно обрабатывать
IndentationError, SyntaxError, NameError нужно исправлять в коде а не пытаться обработать.
Важно помнить, что использовать обработку исключений для замалчивания ошибок программиста недопустимо.
Список исключений
Список встроенных в Python исключений
Существуют следующие типы объектов Exception
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
IndexError
Объекты, которые поддерживают
протокол
Sequence должны поднимать исключение IndexError при использовании несуществующего индекса.
IndexError как и
KeyError
относится к ошибкам поиска LookupError
Пример
>>> a = [0, 1, 2]
>>> a[3]
Traceback (most recent call last):
File «<stdin>», line 1, in <module>
IndexError: list index out of range
ValueError
ValueError поднимается когда объект правильного типа, но содержит неправильное значение
>>> int(«text»)
Traceback (most recent call last):
File «<stdin>», line 1, in <module>
ValueError: invalid literal for int() with base 10: ‘text’
KeyError
KeyError поднимается когда поиск по ключам не даёт результата
>>> sites = dict(urn=1, heihei=2, eth1=3)
>>> sites[«topbicycle»]
Traceback (most recent call last):
File «<stdin>», line 1, in <module>
KeyError: ‘topbicycle’
TypeError
TypeError поднимается когда для успешного выполнения операции нужен объект
определённого типа, а предоставлен другой тип.
pi = 3.1415
text = «Pi is approximately « + pi
python str_ex.py
Traceback (most recent call last):
File «str_ex.py», line 3, in <module>
text = «Pi is approximately » + pi
TypeError: can only concatenate str (not «float») to str
Пример из статьи
str()
SyntaxError
SyntaxError поднимается когда допущена ошибка в синтаксисе языка, например, использован
несуществующий оператор.
Python 3.8.10 (default, Nov 14 2022, 12:59:47)
[GCC 9.4.0] on linux
Type «help», «copyright», «credits» or «license» for more information.
>>>
>>>
>>> 0 <> 0
File «<stdin>», line 1
0 <> 0
^
SyntaxError: invalid syntax
Пример из статьи
__future__
Python | |
Интерактивный режим | |
str: строки | |
: перенос строки | |
Списки [] | |
if, elif, else | |
Циклы | |
Функции | |
Пакеты | |
*args **kwargs | |
ООП | |
enum | |
Опеределить тип переменной Python | |
Тестирование с помощью Python | |
Работа с REST API на Python | |
Файлы: записать, прочитать, дописать, контекстный менеджер… | |
Скачать файл по сети | |
SQLite3: работа с БД | |
datetime: Дата и время в Python | |
json.dumps | |
Selenium + Python | |
Сложности при работе с Python | |
DJANGO | |
Flask | |
Скрипт для ZPL принтера | |
socket :Python Sockets | |
Виртуальное окружение | |
subprocess: выполнение bash команд из Python | |
multiprocessing: несколько процессов одновременно | |
psutil: cистемные ресурсы | |
sys.argv: аргументы командной строки | |
PyCharm: IDE | |
pydantic: валидация данных | |
paramiko: SSH из Python | |
enumerate | |
logging: запись в лог | |
Обучение программированию на Python |