Try except python вывод ошибки

One has pretty much control on which information from the traceback to be displayed/logged when catching exceptions.

The code

with open("not_existing_file.txt", 'r') as text:
    pass

would produce the following traceback:

Traceback (most recent call last):
  File "exception_checks.py", line 19, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

Print/Log the full traceback

As others already mentioned, you can catch the whole traceback by using the traceback module:

import traceback
try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    traceback.print_exc()

This will produce the following output:

Traceback (most recent call last):
  File "exception_checks.py", line 19, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

You can achieve the same by using logging:

try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    logger.error(exception, exc_info=True)

Output:

__main__: 2020-05-27 12:10:47-ERROR- [Errno 2] No such file or directory: 'not_existing_file.txt'
Traceback (most recent call last):
  File "exception_checks.py", line 27, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

Print/log error name/message only

You might not be interested in the whole traceback, but only in the most important information, such as Exception name and Exception message, use:

try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    print("Exception: {}".format(type(exception).__name__))
    print("Exception message: {}".format(exception))

Output:

Exception: FileNotFoundError
Exception message: [Errno 2] No such file or directory: 'not_existing_file.txt'

Обработка исключений

При выполнении заданий к главам вы, скорее всего, нередко сталкивались с возникновением различных ошибок. В этой главе мы изучим подход, который позволяет обрабатывать ошибки после их возникновения.

Напишем программу, которая будет считать обратные значения для целых чисел из заданного диапазона и выводить их в одну строку с разделителем ‘;’. Один из вариантов кода для решения этой задачи выглядит так:

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

Обратите внимание: при втором способе импортированные объекты попадают в пространство имён новой программы. Это означает, что они будут объектами новой программы и в программе не должно быть других объектов с такими же именами.

Рассмотрим написанное выше на примере. Пусть имеется программа module_hello.py, в которой находится функция hello(name), возвращающая строку приветствия пользователя по имени. В самой программе кроме функции присутствует вызов этой функции и печать результата её работы. Импортируем из модуля module_hello.py функцию hello(name) в другую программу program.py и также используем для вывода приветствия пользователя.

Код программы module_hello.py:

def hello(name):
    return f"Привет, {name}!"


print(hello(input("Введите своё имя: ")))

Код программы program.py:

from module_hello import hello

print(hello(input("Добрый день. Введите имя: ")))

При выполнении program.py нас ожидает неожиданное действие. Программа сначала запросит имя пользователя, а затем сделает это ещё раз, но с приветствием из program.py.

Введите своё имя: Андрей
Привет, Андрей!
Добрый день. Введите имя: Андрей
Привет, Андрей!

Наша ошибка заключается в том, что программа module_hello.py выполняется полностью, включая основной код с вызовом функции и выводом результата. Исправим программу module_hello.py, добавив проверку, запущена программа или импортирована как модуль:

def hello(name):
    return f"Привет, {name}!"


if __name__ == "__main__":
    print(hello(input("Введите своё имя: ")))

Теперь при импорте модуля module_hello.py код в теле условного оператора выполняться не будет. А основной код этой программы выполнится только при запуске файла как отдельной программы.
Для большего удобства обычно в теле указанного условного оператора вызывают функцию main(), а основной код программы оформляют уже внутри этой функции.
Тогда наш модуль можно переписать так:

def hello(name):
    return f"Привет, {name}!"


def main():
    print(hello(input("Введите своё имя: ")))


if __name__ == "__main__":
    main()

Обратите внимание: при импорте модуля мы можем с помощью символа * указать, что необходимо импортировать все объекты. Например, так:

from some_module import *

Однако делать так крайне не рекомендуется, потому что все объекты модуля добавляются в пространство имён нашей программы, что может приводить к конфликтам.

Содержание:развернуть

  • Как устроен механизм исключений
  • Как обрабатывать исключения в 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 в Python

В нашем примере Traceback содержится следующую информацию (читаем снизу вверх):

  1. TypeError — тип ошибки (означает, что операция не может быть выполнена с переменной этого типа);
  2. can only concatenate str (not "int") to str — подробное описание ошибки (конкатенировать можно только строку со строкой);
  3. Стек вызова функций (1-я линия — место, 2-я линия — код). В нашем примере видно, что в файле «test.py» на 11-й линии был вызов функции «a» со строковым аргументом «10». Далее был вызов функции «b». print(value + 1) это последнее, что было выполнено — тут и произошла ошибка.
  4. 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

В программе может возникнуть несколько исключений, например:

  1. Ошибка преобразования введенных значений к типу float («ValueError»);
  2. Деление на ноль («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 мин

Количество просмотров 6.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:

  1. Проектируйте код в расчёте на возможное возникновение ошибок. Заранее планируйте устройство кода с учётом возможных сбоев и проектируйте программы так, чтобы они могли бы достойно обрабатывать эти сбои. Это означает — предугадывать возможные пограничные случаи и реализовывать подходящие обработчики ошибок.

  2. Используйте содержательные сообщения об ошибках. Сделайте так, чтобы программа выводила бы, на экран, или в файл журнала, подробные сообщения об ошибках, которые помогут пользователям понять — что и почему пошло не так. Старайтесь не применять обобщённые сообщения об ошибках, наподобие Error occurred или Something bad happened. Вместо этого подумайте об удобстве пользователя и покажите сообщение, в котором будет дан совет по решению проблемы или будет приведена ссылка на документацию. Постарайтесь соблюсти баланс между выводом подробных сообщений и перегрузкой пользовательского интерфейса избыточными данными.

  3. Минимизируйте побочные эффекты. Постарайтесь свести к минимуму последствия сбойных операций, изолируя проблемные разделы кода посредством конструкции try-finally или try с использованием with. Сделайте так, чтобы после выполнения кода, было ли оно удачным или нет, обязательно выполнялись бы «очистительные» операции.

  4. Тщательно тестируйте код. Обеспечьте корректное поведение обработчиков ошибок в различных сценариях использования программы, подвергнув код всеобъемлющему тестированию.

  5. Регулярно выполняйте рефакторинг кода. Выполняйте рефакторинг фрагментов кода, подверженных ошибкам, чтобы улучшить их надёжность и производительность. Постарайтесь, чтобы ваша кодовая база была бы устроена по модульному принципу, чтобы её отдельные части слабо зависели бы друг от друга. Это позволяет независимым частям код самостоятельно эволюционировать, не оказывая негативного воздействия на другие его части.

  6. Логируйте важные события. Следите за интересными событиями своего приложения, записывая сведения о них в файл журнала или выводя в консоль. Это поможет вам выявлять проблемы на ранних стадиях их возникновения, не тратя время на длительный анализ большого количества неструктурированных логов.

Итоги

Написание кода обработки ошибок — это неотъемлемая часть индустрии разработки ПО, и, в частности — разработки на Python. Это позволяет разработчикам создавать более надёжные и стабильные программы. Следуя индустриальным стандартам и рекомендациям по обработке исключений, разработчик может сократить время, необходимое на отладку кода, способен обеспечить написание качественных программ и сделать так, чтобы пользователям было бы приятно работать с этими программами.

О, а приходите к нам работать? 🤗 💰

Мы в wunderfund.io занимаемся высокочастотной алготорговлей с 2014 года. Высокочастотная торговля — это непрерывное соревнование лучших программистов и математиков всего мира. Присоединившись к нам, вы станете частью этой увлекательной схватки.

Мы предлагаем интересные и сложные задачи по анализу данных и low latency разработке для увлеченных исследователей и программистов. Гибкий график и никакой бюрократии, решения быстро принимаются и воплощаются в жизнь.

Сейчас мы ищем плюсовиков, питонистов, дата-инженеров и мл-рисерчеров.

Присоединяйтесь к нашей команде.

В этом руководстве мы расскажем, как обрабатывать исключения в Python с помощью try и except. Рассмотрим общий синтаксис и простые примеры, обсудим, что может пойти не так, и предложим меры по исправлению положения.

Зачастую разработчик может предугадать возникновение ошибок при работе даже синтаксически и логически правильной программы. Эти ошибки могут быть вызваны неверными входными данными или некоторыми предсказуемыми несоответствиями.

Для обработки большей части этих ошибок как исключений в Python есть блоки try и except.

Для начала разберем синтаксис операторов try и except в Python. Общий шаблон представлен ниже:

try:
	# В этом блоке могут быть ошибки
    
except <error type>:
	# Сделай это для обработки исключения;
	# выполняется, если блок try выбрасывает ошибку
    
else:
	# Сделай это, если блок try выполняется успешно, без ошибок
   
finally:
	# Этот блок выполняется всегда

Давайте посмотрим, для чего используются разные блоки.

Блок try

Блок try — это блок кода, который вы хотите попробовать выполнить. Однако во время выполнения из-за какого-нибудь исключения могут возникнуть ошибки. Поэтому этот блок может не работать должным образом.

Блок except

Блок except запускается, когда блок try не срабатывает из-за исключения. Инструкции в этом блоке часто дают некоторый контекст того, что пошло не так внутри блока try.

Если собираетесь перехватить ошибку как исключение, в блоке except нужно обязательно указать тип этой ошибки. В приведенном выше сниппете место для указания типа ошибки обозначено плейсхолдером <error type> .

except можно использовать и без указания типа ошибки. Но лучше так не делать. При таком подходе не учитывается, что возникающие ошибки могут быть разных типов. То есть вы будете знать, что что-то пошло не так, но что именно произошло, какая была ошибка — вам будет не известно.

При попытке выполнить код внутри блока try также существует вероятность возникновения нескольких ошибок.

Например, вы можете попытаться обратиться к элементу списка по индексу, выходящему за пределы допустимого диапазона, использовать неправильный ключ словаря и попробовать открыть несуществующий файл – и все это внутри одного блока try.

В результате вы можете столкнуться с IndexError, KeyError и FileNotFoundError. В таком случае нужно добавить столько блоков except, сколько ошибок ожидается – по одному для каждого типа ошибки.

Блок else

Блок else запускается только в том случае, если блок try выполняется без ошибок. Это может быть полезно, когда нужно выполнить ещё какие-то действия после успешного выполнения блока try. Например, после успешного открытия файла вы можете прочитать его содержимое.

Блок finally

Блок finally выполняется всегда, независимо от того, что происходит в других блоках. Это полезно, когда вы хотите освободить ресурсы после выполнения определенного блока кода.

Примечание: блоки else и finally не являются обязательными. В большинстве случаев вы можете использовать только блок try, чтобы что-то сделать, и перехватывать ошибки как исключения внутри блока except.

[python_ad_block]

Итак, теперь давайте используем полученные знания для обработки исключений в Python. Приступим!

Обработка ZeroDivisionError

Рассмотрим функцию divide(), показанную ниже. Она принимает два аргумента – num и div – и возвращает частное от операции деления num/div.

def divide(num,div):
    return num/div

Вызов функции с разными аргументами возвращает ожидаемый результат:

res = divide(100,8)
print(res)

# Output
# 12.5

res = divide(568,64)
print(res)

# Output
# 8.875

Этот код работает нормально, пока вы не попробуете разделить число на ноль:

divide(27,0)

Вы видите, что программа выдает ошибку ZeroDivisionError:

# Output
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-19-932ea024ce43> in <module>()
----> 1 divide(27,0)

<ipython-input-1-c98670fd7a12> in divide(num, div)
      1 def divide(num,div):
----> 2   return num/div

ZeroDivisionError: division by zero

Можно обработать деление на ноль как исключение, выполнив следующие действия:

  1. В блоке try поместите вызов функции divide(). По сути, вы пытаетесь разделить num на div (try в переводе с английского — «пытаться», — прим. перев.).
  2. В блоке except обработайте случай, когда div равен 0, как исключение.
  3. В результате этих действий при делении на ноль больше не будет выбрасываться ZeroDivisionError. Вместо этого будет выводиться сообщение, информирующее пользователя, что он попытался делить на ноль.

Вот как все это выглядит в коде:

try:
    res = divide(num,div)
    print(res)
except ZeroDivisionError:
    print("You tried to divide by zero :( ")

При корректных входных данных наш код по-прежнему работает великолепно:

divide(10,2)
# Output
# 5.0

Когда же пользователь попытается разделить на ноль, он получит уведомление о возникшем исключении. Таким образом, программа завершается корректно и без ошибок.

divide(10,0)
# Output
# You tried to divide by zero :(

Обработка TypeError

В этом разделе мы разберем, как использовать try и except для обработки TypeError в Python.

Рассмотрим функцию add_10(). Она принимает число в качестве аргумента, прибавляет к нему 10 и возвращает результат этого сложения.

def add_10(num):
    return num + 10

Вы можете вызвать функцию add_10() с любым числом, и она будет работать нормально, как показано ниже:

result = add_10(89)
print(result)

# Output
# 99

Теперь попробуйте вызвать функцию add_10(), передав ей в качестве аргумента не число, а строку.

add_10 ("five")

Ваша программа вылетит со следующим сообщением об ошибке:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-9844e949c84e> in <module>()
----> 1 add_10("five")

<ipython-input-13-2e506d74d919> in add_10(num)
      1 def add_10(num):
----> 2   return num + 10

TypeError: can only concatenate str (not "int") to str

Сообщение об ошибке TypeError: can only concatenate str (not "int") to str говорит о том, что можно сложить только две строки, а не добавить целое число к строке.

Обработаем TypeError:

  • В блок try мы помещаем вызов функции add_10() с my_num в качестве аргумента. Если аргумент допустимого типа, исключений не возникнет.
  • В противном случае срабатывает блок except, в который мы помещаем вывод уведомления для пользователя о том, что аргумент имеет недопустимый тип.

Это показано ниже:

my_num = "five"
try:
    result = add_10(my_num)
    print(result)
except TypeError:
    print("The argument `num` should be a number")

Поскольку теперь вы обработали TypeError как исключение, при передаче невалидного аргумента ошибка не возникает. Вместо нее выводится сообщение, что аргумент имеет недопустимый тип.

The argument `num` should be a number

Обработка IndexError

Если вам приходилось работать со списками или любыми другими итерируемыми объектами, вы, вероятно, сталкивались с IndexError.

Это связано с тем, что часто бывает сложно отслеживать все изменения в итерациях. И вы можете попытаться получить доступ к элементу по невалидному индексу.

В этом примере список my_list состоит из 4 элементов. Допустимые индексы — 0, 1, 2 и 3 и -1, -2, -3, -4, если вы используете отрицательную индексацию.

Поскольку 2 является допустимым индексом, вы видите, что элемент с этим индексом (C++) распечатывается:

my_list = ["Python","C","C++","JavaScript"]
print(my_list[2])

# Output
# C++

Но если вы попытаетесь получить доступ к элементу по индексу, выходящему за пределы допустимого диапазона, вы столкнетесь с IndexError:

print(my_list[4])
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-7-437bc6501dea> in <module>()
      1 my_list = ["Python","C","C++","JavaScript"]
----> 2 print(my_list[4])

IndexError: list index out of range

Теперь вы уже знакомы с шаблоном, и вам не составит труда использовать try и except для обработки данной ошибки.

В приведенном ниже фрагменте кода мы пытаемся получить доступ к элементу по индексу search_idx.

search_idx = 3
try:
    print(my_list[search_idx])
except IndexError:
    print("Sorry, the list index is out of range")

Здесь search_idx = 3 является допустимым индексом, поэтому в результате выводится соответствующий элемент — JavaScript.

Если search_idx находится за пределами допустимого диапазона индексов, блок except перехватывает IndexError как исключение, и больше нет длинных сообщений об ошибках.

search_idx = 4
try:
    print(my_list[search_idx])
except IndexError:
    print("Sorry, the list index is out of range")

Вместо этого отображается сообщение о том, что search_idx находится вне допустимого диапазона индексов:

Sorry, the list index is out of range

Обработка KeyError

Вероятно, вы уже сталкивались с KeyError при работе со словарями в Python.

Рассмотрим следующий пример, где у нас есть словарь my_dict.

my_dict ={"key1":"value1","key2":"value2","key3":"value3"}
search_key = "non-existent key"
print(my_dict[search_key])

В словаре my_dict есть 3 пары «ключ-значение»: key1:value1, key2:value2 и key3:value3.

Теперь попытаемся получить доступ к значению, соответствующему несуществующему ключу non-existent key.

Как и ожидалось, мы получим KeyError:

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-2-2a61d404be04> in <module>()
      1 my_dict ={"key1":"value1","key2":"value2","key3":"value3"}
      2 search_key = "non-existent key"
----> 3 my_dict[search_key]

KeyError: 'non-existent key'

Вы можете обработать KeyError почти так же, как и IndexError.

  • Пробуем получить доступ к значению, которое соответствует ключу, определенному search_key.
  • Если search_key — валидный ключ, мы распечатываем соответствующее значение.
  • Если ключ невалиден и возникает исключение — задействуется блок except, чтобы сообщить об этом пользователю.

Все это можно видеть в следующем коде:

try:
    print(my_dict[search_key])
except KeyError:
    print("Sorry, that's not a valid key!")

# Output:
# Sorry, that's not a valid key!

Если вы хотите предоставить дополнительный контекст, например имя невалидного ключа, это тоже можно сделать. Возможно, ключ оказался невалидным из-за ошибки в написании. Если вы укажете этот ключ в сообщении, это поможет пользователю исправить опечатку.

Вы можете сделать это, перехватив невалидный ключ как <error_msg> и используя его в сообщении, которое печатается при возникновении исключения:

try:
    print(my_dict[search_key])
except KeyError as error_msg:
    print(f"Sorry,{error_msg} is not a valid key!")

Обратите внимание, что теперь в сообщении об ошибки указано также и имя несуществующего ключа:

Sorry, 'non-existent key' is not a valid key!

Обработка FileNotFoundError

При работе с файлами в Python часто возникает ошибка FileNotFoundError.

В следующем примере мы попытаемся открыть файл my_file.txt, указав его путь в функции open(). Мы хотим прочитать файл и вывести его содержимое.

Однако мы еще не создали этот файл в указанном месте.

my_file = open("/content/sample_data/my_file.txt")
contents = my_file.read()
print(contents)

Поэтому, попытавшись запустить приведенный выше фрагмент кода, мы получим FileNotFoundError:

---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
<ipython-input-4-4873cac1b11a> in <module>()
----> 1 my_file = open("my_file.txt")

FileNotFoundError: [Errno 2] No such file or directory: 'my_file.txt'

А с помощью try и except мы можем сделать следующее:

  • Попробуем открыть файл в блоке try.
  • Обработаем FileNotFoundError в блоке except, сообщив пользователю, что он попытался открыть несуществующий файл.
  • Если блок try завершается успешно и файл действительно существует, прочтем и распечатаем содержимое.
  • В блоке finally закроем файл, чтобы не терять ресурсы. Файл будет закрыт независимо от того, что происходило на этапах открытия и чтения.
try:
    my_file = open("/content/sample_data/my_file.txt")
except FileNotFoundError:
    print(f"Sorry, the file does not exist")
else:
    contents = my_file.read()
    print(contents)
finally:
    my_file.close()

Обратите внимание: мы обработали ошибку как исключение, и программа завершает работу, отображая следующее сообщение:

Sorry, the file does not exist

Теперь рассмотрим случай, когда срабатывает блок else. Файл my_file.txt теперь присутствует по указанному ранее пути.

Вот содержимое этого файла:

Теперь повторный запуск нашего кода работает должным образом.

На этот раз файл my_file.txt присутствует, поэтому запускается блок else и содержимое распечатывается, как показано ниже:

Надеемся, теперь вы поняли, как обрабатывать исключения при работе с файлами.

Заключение

В этом руководстве мы рассмотрели, как обрабатывать исключения в Python с помощью try и except.

Также мы разобрали на примерах, какие типы исключений могут возникать и как при помощи except ловить наиболее распространенные ошибки.

Надеемся, вам понравился этот урок. Успехов в написании кода!

Перевод статьи «Python Try and Except Statements – How to Handle Exceptions in Python».

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

  1. Printing custom error messages and
  2. 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.

https://docs.python.org/3/reference/datamodel.html#object.str

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.

https://docs.python.org/3/reference/datamodel.html#object.repr

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!

В этом материале речь пойдет о блоках try/except, finally и raise. Вместе с тем будет рассмотрено, как создавать собственные исключения в Python.

2. Обработка исключений в Python

Рассмотрим разные типы исключений в Python, которые появляются при срабатывании исключения в коде Python.

3. Блоки try/except

Если код может привести к исключению, его лучше заключить в блок try. Рассмотрим на примере.

try:
    for i in range(3):
        print(3/i)
except:
    print("Деление на 0")
    print("Исключение было обработано")

Программа вывела сообщение, потому что было обработано исключение.

Следом идет блок except. Если не определить тип исключения, то он будет перехватывать любые. Другими словами, это общий обработчик исключений.

Если код в блоке try приводит к исключению, интерпретатор ищет блок except, который указан следом. Оставшаяся часть кода в try исполнена не будет.

Исключения Python особенно полезны, если программа работает с вводом пользователя, ведь никогда нельзя знать, что он может ввести.

a. Несколько except в Python

У одного блока try может быть несколько блоков except. Рассмотрим примеры с несколькими вариантами обработки.

a, b = 1, 0
try:
    print(a/b)
    print("Это не будет напечатано")
    print('10'+10)
except TypeError:
    print("Вы сложили значения несовместимых типов")
except ZeroDivisionError:
    print("Деление на 0")

Когда интерпретатор обнаруживает исключение, он проверяет блоки except соответствующего блока try. В них может быть объявлено, какие типы исключений они обрабатывают. Если интерпретатор находит соответствующее исключение, он исполняет этот блок except.

В первом примере первая инструкция приводит к ZeroDivisionError. Эта ошибка обрабатывается в блоке except, но инструкции в try после первой не исполняются. Так происходит из-за того, что после первого исключения дальнейшие инструкции просто пропускаются. И если подходящий или общий блоки except не удается найти, исключение не обрабатывается. В таком случае оставшаяся часть программы не будет запущена. Но если обработать исключение, то код после блоков except и finally исполнится. Попробуем.

a, b = 1, 0
try:
   print(a/b)
except:
   print("Вы не можете разделить на 0")
print("Будет ли это напечатано?")

Рассмотрим вывод:

Вы не можете разделить на 0
Будет ли это напечатано?

b. Несколько исключений в одном except

Можно использовать один блок except для обработки нескольких исключений. Для этого используются скобки. Без них интерпретатор вернет синтаксическую ошибку.

try:
    print('10'+10)
    print(1/0)
except (TypeError,ZeroDivisionError):
    print("Неверный ввод")
Неверный ввод

c. Общий except после всех блоков except

В конце концов, завершить все отдельные блоки except можно одним общим. Он используется для обработки всех исключений, которые не были перехвачены отдельными except.

try:
    print('1'+1)
    print(sum)
    print(1/0)
except NameError:
    print("sum не существует")
except ZeroDivisionError:
    print("Вы не можете разделить на 0")
except:
    print("Что-то пошло не так...")
Что-то пошло не так...

Здесь первая инструкция блока пытается осуществить операцию конкатенации строки python с числом. Это приводит к ошибке TypeError. Как только интерпретатор сталкивается с этой проблемой, он проверяет соответствующий блок except, который ее обработает.

Отдельную инструкцию нельзя разместить между блоками try и except.

try:
    print("1")
print("2")
except:
    print("3")

Это приведет к синтаксической ошибке.

Но может быть только один общий или блок по умолчанию типа except. Следующий код вызовет ошибку «default 'except:' must be last»:

try:
    print(1/0)
except:
    raise
except:
    print("Исключение поймано")
finally:
    print("Хорошо")
print("Пока")

4. Блок finally в Python

После последнего блока except можно добавить блок finally. Он исполняет инструкции при любых условиях.

try:
    print(1/0)
except ValueError:
    print("Это ошибка значения")
finally:
    print("Это будет напечатано в любом случае.")
Это будет напечатано в любом случае.

Traceback (most recent call last):  
  File “”, line 2, in   
    print(1/0)
ZeroDivisionError: division by zero

Стоит обратить внимание, что сообщение с ошибкой выводится после исполнения блока finally. Почему же тогда просто не использовать print? Но как видно по последнему примеру, блок finally запускается даже в том случае, если перехватить исключение не удается.

А что будет, если исключение перехватывается в except?

try:
    print(1/0)
except ZeroDivisionError:
    print(2/0)
finally:
    print("Ничего не происходит")
Ничего не происходит

Traceback (most recent call last):
  File "", line 2, in 
    print(1/0)
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "", line 4, in 
    print(2/0)
ZeroDivisionError: division by zero

Как видите, код в блоке finally исполняется в любом случае.

5. Ключевое слово raise в Python

Иногда нужно будет разбираться с проблемами с помощью вызова исключения. Обычная инструкция print тут не сработает.

raise ZeroDivisionError
Traceback (most recent call last):
  File "", line 1, in 
    raise ZeroDivisionError
ZeroDivisionError

Разберемся на примере операции деления:

a,b=int(input()),int(input())  # вводим 1 затем 0
if b==0:
    raise ZeroDivisionError
Traceback (most recent call last):
  File "", line 3, in 
    raise ZeroDivisionError
ZeroDivisionError

Здесь ввод пользователя в переменные a и b конвертируется в целые числа. Затем проверяется, равна ли b нулю. Если да, то вызывается ZeroDivisionError.

Что будет, если то же самое добавить в блоки try-except? Добавим следующее в код. Если запустить его, ввести 1 и 0, будет следующий вывод:

a,b=int(input()),int(input())
try:
    if b==0:
        raise ZeroDivisionError
except:
   print("Деление на 0")
print("Будет ли это напечатано?")
1
0
Деление на 0
Будет ли это напечатано?

Рассмотрим еще несколько примеров, прежде чем двигаться дальше:

raise KeyError
Traceback (most recent call last):
  File “”, line 1, in 
    raise KeyError
KeyError

a. Raise без определенного исключения в Python

Можно использовать ключевое слово raise и не указывая, какое исключение вызвать. Оно вызовет исключение, которое произошло. Поэтому его можно использовать только в блоке except.

try:
    print('1'+1)
except:
    raise
Traceback (most recent call last):
  File “”, line 2, in 
    print(‘1’+1)
TypeError: must be str, not int

b. Raise с аргументом в Python

Также можно указать аргумент к определенному исключению в raise. Делается это с помощью дополнительных деталей исключения.

raise ValueError("Несоответствующее значение")
Traceback (most recent call last):
  File "", line 1, in 
    raise ValueError("Несоответствующее значение")
ValueError: Несоответствующее значение

6. assert в Python

Утверждение (assert) — это санитарная проверка для вашего циничного, параноидального «Я». Оно принимает инструкцию в качестве аргумента и вызывает исключение Python, если возвращается значение False. В противном случае выполняет операцию No-operation (NOP).

assert(True)
#  код работает дальше

Если бы инструкция была False?

assert(1==0)
Traceback (most recent call last):
  File “”, line 1, in 
    assert(1==0)
AssertionError

Возьмем другой пример:

try:
    print(1)
    assert 2+2==4
    print(2)
    assert 1+2==4
    print(3)
except:
    print("assert False.")
    raise
finally:
    print("Хорошо")
print("Пока")

Вывод следующий:

1
2
assert False.
Хорошо
Traceback (most recent call last):
  File “”, line 5, in 
    assert 1+2==4
AssertionError

Утверждения можно использовать для проверки валидности ввода и вывода в функции.

a. Второй аргумент для assert

Можно предоставить второй аргумент, чтобы дать дополнительную информацию о проблеме.

assert False,"Это проблема"
Traceback (most recent call last):
  File “”, line 1, in 
    assert False,”Это проблема”
AssertionError: Это проблема

7. Объявление собственных исключений Python

Наконец, рассмотрим процесс создания собственных исключений. Для этого создадим новый класс из класса Exception. Потом его можно будет вызывать как любой другой тип исключения.

class MyError(Exception):
    print("Это проблема")

raise MyError("ошибка MyError")
Traceback (most recent call last):
  File “”, line 1, in 
    raise MyError(“ошибка MyError”)
MyError: ошибка MyError

Вот и все, что касается обработки исключений в Python.

8. Вывод: обработка исключений Python

Благодаря этой статье вы сможете обеспечить дополнительную безопасность своему коду. Все благодаря возможности обработки исключений Python, их вызова и создания собственных.

Содержание

Введение
Пример с базовым 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)
return1

>>> 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

Toggle table of contents sidebar

try/except#

Если вы повторяли примеры, которые использовались ранее, то наверняка
были ситуации, когда выскакивала ошибка. Скорее всего, это была ошибка
синтаксиса, когда не хватало, например, двоеточия.

Как правило, Python довольно понятно реагирует на подобные ошибки, и их
можно исправить.

Тем не менее, даже если код синтаксически написан правильно, могут
возникать ошибки. В Python эти ошибки называются исключения (exceptions).

Примеры исключений:

In [1]: 2/0
-----------------------------------------------------
ZeroDivisionError: division by zero

In [2]: 'test' + 2
-----------------------------------------------------
TypeError: must be str, not int

В данном случае возникло два исключения: ZeroDivisionError и
TypeError.

Чаще всего можно предсказать, какого рода исключения возникнут во время
исполнения программы.

Например, если программа на вход ожидает два числа, а на выходе выдает
их сумму, а пользователь ввел вместо одного из чисел строку, появится
ошибка TypeError, как в примере выше.

Python позволяет работать с исключениями. Их можно перехватывать и
выполнять определенные действия в том случае, если возникло исключение.

Примечание

Когда в программе возникает исключение, она сразу завершает работу.

Для работы с исключениями используется конструкция try/except:

In [3]: try:
   ...:     2/0
   ...: except ZeroDivisionError:
   ...:     print("You can't divide by zero")
   ...:
You can't divide by zero

Конструкция try работает таким образом:

  • сначала выполняются выражения, которые записаны в блоке try

  • если при выполнения блока try не возникло никаких исключений, блок except пропускается,
    и выполняется дальнейший код

  • если во время выполнения блока try в каком-то месте возникло исключение,
    оставшаяся часть блока try пропускается

    • если в блоке except указано исключение, которое возникло, выполняется код в блоке except

    • если исключение, которое возникло, не указано в блоке except,
      выполнение программы прерывается и выдается ошибка

Обратите внимание, что строка Cool! в блоке try не выводится:

In [4]: try:
   ...:     print("Let's divide some numbers")
   ...:     2/0
   ...:     print('Cool!')
   ...: except ZeroDivisionError:
   ...:     print("You can't divide by zero")
   ...:
Let's divide some numbers
You can't divide by zero

В конструкции try/except может быть много except, если нужны разные
действия в зависимости от типа ошибки.

Например, скрипт divide.py делит два числа введенных пользователем:

# -*- coding: utf-8 -*-

try:
    a = input("Введите первое число: ")
    b = input("Введите второе число: ")
    print("Результат: ", int(a)/int(b))
except ValueError:
    print("Пожалуйста, вводите только числа")
except ZeroDivisionError:
    print("На ноль делить нельзя")

Примеры выполнения скрипта:

$ python divide.py
Введите первое число: 3
Введите второе число: 1
Результат:  3

$ python divide.py
Введите первое число: 5
Введите второе число: 0
На ноль делить нельзя

$ python divide.py
Введите первое число: qewr
Введите второе число: 3
Пожалуйста, вводите только числа

В данном случае исключение ValueError возникает, когда пользователь
ввел строку вместо числа, во время перевода строки в число.

Исключение ZeroDivisionError возникает в случае, если второе число было
равным 0.

Если нет необходимости выводить различные сообщения на ошибки ValueError
и ZeroDivisionError, можно сделать так (файл divide_ver2.py):

# -*- coding: utf-8 -*-

try:
    a = input("Введите первое число: ")
    b = input("Введите второе число: ")
    print("Результат: ", int(a)/int(b))
except (ValueError, ZeroDivisionError):
    print("Что-то пошло не так...")

Проверка:

$ python divide_ver2.py
Введите первое число: wer
Введите второе число: 4
Что-то пошло не так...

$ python divide_ver2.py
Введите первое число: 5
Введите второе число: 0
Что-то пошло не так...

Примечание

В блоке except можно не указывать конкретное исключение или
исключения. В таком случае будут перехватываться все исключения.

Это делать не рекомендуется!

try/except/else#

В конструкции try/except есть опциональный блок else. Он выполняется в
том случае, если не было исключения.

Например, если необходимо выполнять в дальнейшем какие-то операции с
данными, которые ввел пользователь, можно записать их в блоке else (файл
divide_ver3.py):

# -*- coding: utf-8 -*-

try:
    a = input("Введите первое число: ")
    b = input("Введите второе число: ")
    result = int(a)/int(b)
except (ValueError, ZeroDivisionError):
    print("Что-то пошло не так...")
else:
    print("Результат в квадрате: ", result**2)

Пример выполнения:

$ python divide_ver3.py
Введите первое число: 10
Введите второе число: 2
Результат в квадрате:  25

$ python divide_ver3.py
Введите первое число: werq
Введите второе число: 3
Что-то пошло не так...

try/except/finally#

Блок finally — это еще один опциональный блок в конструкции try. Он
выполняется всегда, независимо от того, было ли исключение или нет.

Сюда ставятся действия, которые надо выполнить в любом случае. Например,
это может быть закрытие файла.

Файл divide_ver4.py с блоком finally:

# -*- coding: utf-8 -*-

try:
    a = input("Введите первое число: ")
    b = input("Введите второе число: ")
    result = int(a)/int(b)
except (ValueError, ZeroDivisionError):
    print("Что-то пошло не так...")
else:
    print("Результат в квадрате: ", result**2)
finally:
    print("Вот и сказочке конец, а кто слушал - молодец.")

Проверка:

$ python divide_ver4.py
Введите первое число: 10
Введите второе число: 2
Результат в квадрате:  25
Вот и сказочке конец, а кто слушал - молодец.

$ python divide_ver4.py
Введите первое число: qwerewr
Введите второе число: 3
Что-то пошло не так...
Вот и сказочке конец, а кто слушал - молодец.

$ python divide_ver4.py
Введите первое число: 4
Введите второе число: 0
Что-то пошло не так...
Вот и сказочке конец, а кто слушал - молодец.

Когда использовать исключения#

Как правило, один и тот же код можно написать и с использованием
исключений, и без них.

Например, этот вариант кода:

while True:
    a = input("Введите число: ")
    b = input("Введите второе число: ")
    try:
        result = int(a)/int(b)
    except ValueError:
        print("Поддерживаются только числа")
    except ZeroDivisionError:
        print("На ноль делить нельзя")
    else:
        print(result)
        break

Можно переписать таким образом без try/except (файл
try_except_divide.py):

while True:
    a = input("Введите число: ")
    b = input("Введите второе число: ")
    if a.isdigit() and b.isdigit():
        if int(b) == 0:
            print("На ноль делить нельзя")
        else:
            print(int(a)/int(b))
            break
    else:
        print("Поддерживаются только числа")

Далеко не всегда аналогичный вариант без использования исключений
будет простым и понятным.

Важно в каждой конкретной ситуации оценивать, какой вариант кода более
понятный, компактный и универсальный — с исключениями или без.

Если вы раньше использовали какой-то другой язык программирования, есть
вероятность, что в нём использование исключений считалось плохим тоном.
В Python это не так. Чтобы немного больше разобраться с этим вопросом,
посмотрите ссылки на дополнительные материалы в конце этого раздела.

raise#

Иногда в коде надо сгенерировать исключение, это можно сделать так:

raise ValueError("При выполнении команды возникла ошибка")

Встроенные исключения#

В Python есть много встроенных исключений,
каждое из которых генерируется в
определенной ситуации.

Например, TypeError обычно генерируется когда ожидался один тип данных, а передали другой

In [1]: "a" + 3
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-5aa8a24e3e06> in <module>
----> 1 "a" + 3
TypeError: can only concatenate str (not "int") to str

ValueError когда значение не соответствует ожидаемому:

In [2]: int("a")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-2-d9136db7b558> in <module>
----> 1 int("a")
ValueError: invalid literal for int() with base 10: 'a'

Понравилась статья? Поделить с друзьями:
  • Try except python все ошибки
  • Try catch php обработка ошибок
  • Try catch php все ошибки
  • Tur hinten re offen перевод ошибка фольксваген
  • Tuple object is not callable python ошибка