Блог

Исключения в Python. Операторы вызова исключений: raise и assert

Это продолжение статьи Исключения в Python. Введение

Оператор raise

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

try:
    value = int(input())
    if value % 2 != 0:
        raise ValueError('Variable <value> must be even!')
    print('Congratulations! Variable <value> is even!')
except Exception as e:
    print(f'{e.__class__}: {e}')

В приведённом примере исключение будет вызвано в том случае, когда целое число не является чётным, то есть в случае выполнения условия if value % 2 != 0:

Для этого после оператора raise указывается класс исключения (в данном случае это ValueError), к которому будет отнесена соответствующая группа значений переменной value. А далее, в скобках, добавлен комментарий, поясняющий причину вызова исключения. Ниже приводятся результаты выполнения кода для значений 4 и 5:

>>>
    5
    <class 'ValueError'>: Variable <value> must be even!

    4
    'Congratulations! Variable <value> is even!'

Если появится другая ошибка, например, если с консоли будет введено значение aaa, то программа перейдёт на обработку этого исключения ещё до оператора raise. И, разумеется, выдаст совершенно другой код ошибки (точнее, выдаст другой комментарий ошибки к общему классу исключений ValueError):

>>>
    aaa
    <class 'ValueError'>: invalid literal for int() with base 10: 'aaa'

Оператор assert

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

try:
    value = int(input())
    assert value % 2 == 0, 'Variable <value> must be even!'
    print('Congratulations! Variable <value> is even!')
except Exception as e:
    print(f'{e.__class__}: {e}')

Варианты выполнения кода для различных входных значений:

>>>
    5
    <class 'AssertionError'>: 'Variable <value> must be even!

    4
    'Congratulations! Variable <value> is even!'

    aaa
    <class 'ValueError'>: invalid literal for int() with base 10: 'aaa'

Таким образом, как видим, оба оператора (raise и assert) очень похожи друг на друга.

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

  1. assert не требует отдельного блока if — условие проверки с этим оператором делается в одну строку;
  2. В случае assert не требуется указывать класс исключения — по умолчанию это всегда AssertionError

Вернуться в начало, на статью Исключения в Python. Введение

Читать дальше >>

Исключения в Python. Перехват специфических ошибок

Это продолжение статьи Исключения в Python. Введение

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

Но прежде чем перейти к примеру кода, который решает этот вопрос, стоит посмотреть, какие вообще существуют классы исключений в Python. Для этого в первую очередь следует обратиться к официальной документации Python: https://docs.python.org/3/library/exceptions.html .

Можно также получить список всех встроенных исключений с помощью следующей команды в консоли Python:

dir(locals()['__builtins__'])

В коде, представленном ниже, в отдельные блоки формируются 3 группы исключений:

  1. Ошибка соответствия значения и типа данных (ValueError, TypeError)
  2. Ошибка деления на ноль ZeroDivisionError
  3. Все остальные виды исключений

while True:
    line = input('Please enter an integer: ')
    if line == 'end':
        break

    try:
        result = 100 / int(line)
        print(result)

    except (ValueError, TypeError) as e:
        print('Exception for Value or Type Error')
        print(e.__class__, e)
        print('Please enter another number! ')

    except ZeroDivisionError as e:
        print('Exception for ZeroDivisionError')
        print(e.__class__, e)
        print('Please enter another number! ')

    except Exception as e:
        print('Exception for other error')
        print(e.__class__, e)
        print('Please enter another number! ')

Продолжить в статье Исключения в Python. Операторы вызова исключений: raise и assert

Вернуться в начало, на статью Исключения в Python. Введение

Читать дальше >>

Исключения в Python. Конструкция try-except-finally

Это продолжение статьи Исключения в Python. Введение

Следующая задача. Допустим, данные необходимо получить не с помощью консольного ввода, а построчно считать из файла и добавить обработанный результат в тот же файл. И если в процессе чтения / обработки возникнет ошибка, то, помимо стандартного сообщения об ошибке, необходимо также в любом случае закрыть этот текстовой файл.

Для тестирования этого кода понадобится добавить в текущий каталог программы текстовой файл tmp.txt следующего содержания:

5
7

В коде, представленом ниже, происходит открытие файла, обработка строк (в данном случае, это деление числа 10 на целое число, взятое из соответствющей строки файла), накопление результата для выгрузки в файл в переменной output и, наконец, добавление полученного результата в конец текстового файла:

try:
    f = open('tmp.txt', 'r+')
    output = ''
    for line in f:
        num = int(line)
        result = 10 / num
        output += f'{result}\n'

    f.write(output)

except Exception as e:
    print(e.__class__, e)

finally:
    print('f.closed = ', f.closed)
    f.close()
    print('f.closed = ', f.closed)

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

>>>
    f.closed =  False
    f.closed =  True

А текстовой файл tmp.txt увеличится на две новых строки:

>>>
    5
    7
    2.0
    1.4285714285714286

Эти добавленные строки - дробные числа в строковом формате. Поэтому, если мы ещё раз запустим скрипт, то на этот раз получим ошибку и сообщение:

>>>
    <class 'ValueError'> invalid literal for int() with base 10: '2.0\n'
    f.closed =  False
    f.closed =  True

Однако, как мы видим, не смотря наличие или отсутствие ошибок исполнения кода, и в первом и во втором случае был выполнен блок finally: файл был закрыт с выводом соответствующего сообщения: f.closed = True.

Продолжить в статье Исключения в Python. Перехват специфических ошибок

Вернуться в начало, на статью Исключения в Python. Введение

Читать дальше >>

Исключения в Python. Конструкция try-except-else

Это продолжение статьи Исключения в Python. Введение

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

Для подобных случаев существует конструкция try-except-else, когда ветвление программы зависит от наличия или отсутствия ошибки в блоке try. Если ошибка найдена, то программа выполняет блок except. Если нет — выполняется блок else:

while True:
    line = input('Please enter an integer: ')
    if line == 'end':
        break

    try:
        result = 100 / int(line)
        print(result)

    except Exception as e:
        print(e.__class__, e)
        print('Please enter another number! ')

    else:
        print('Successful input!')
        break

Продолжить в статье Исключения в Python. Конструкция try-except-finally

Вернуться в начало, на статью Исключения в Python. Введение

Читать дальше >>

Исключения в Python. Конструкция try-except

Это продолжение статьи Исключения в Python. Введение

Перехватить все возможные исключения (Exceptions) в этой ситуации мы сможем с помощью конструкции try-except:

while True:
    line = input('Please enter an integer: ')
    if line == 'end':
        break

    try:
        result = 100 / int(line)
        print(result)

    except Exception as e:
        print(e.__class__, e)
        print('Please enter another number! ')

Как видим, код не стал больше, скорее даже наоборот — он стал понятнее.

Однако помимо этого мы получили возможность перехватывать абсолютно все ошибки, которые могут возникнуть в блоке try при вычислении переменной result:

  1.) Ошибка деления на ноль:

>>>
    Please enter an integer: 0
    <class 'ZeroDivisionError'> division by zero
    Please enter another number!

  2.) Ошибка ввода дробного значения:

>>>
    Please enter an integer: 1.1
    <class 'ValueError'> invalid literal for int() with base 10: '1.1'
    Please enter another number!

  3.) Ошибка ввода нечисловых символов:

>>>
    Please enter an integer: asd
    <class 'ValueError'> invalid literal for int() with base 10: 'asd'
    Please enter another number!

И самое главное, благодаря этой конструкции будут перехвачены вообще все мыслимые и немыслимые ошибки ввода. Даже те, которые могут возникнуть в новых версиях интерпретатора Python, программа не будет завершена аварийно и пользователь в конце концов сможет ввести валидное значение делителя!

Продолжить в статье Исключения в Python. Конструкция try-except-else

Вернуться в начало, на статью Исключения в Python. Введение

Читать дальше >>

Исключения в Python. Введение

Python Exception Handling. Introduction

Не ошибается только тот, кто ничего не делает. Поэтому ошибки в коде — естественны и неизбежны.

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

В первом случае это Syntax Errors (синтаксические ошибки), а во втором — Exceptions (исключения).

Синтаксическая ошибка — это ошибка в синтаксисе последовательности символов или токенов. Иными словами, синтаксическая ошибка — это несоответствие правилам записи кода, без исправления которой код не может быть запущен.

Исключения возникают, если программа синтаксически верна, но в некоторых случаях (при некоторых значениях переменных) исполнение кода приводит к ошибке. То есть исключения возникают в тех случаях, когда программист не учёл или не предусмотрел какое-либо событие, которое в итоге привело в ошибке и остановке программы.

Классический пример — ошибка деления на 0:

>>> 5 / 0
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    ZeroDivisionError: division by zero

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

  • так это причину, её вызвавшую,
  • а также место в коде, где она появилась.
И, как видно из примера выше, интерпретатор Python уже имеет в своём арсенале встроенную систему классификацию таких ошибок, благодаря чему мы знаем:

  1. Место возникновения ошибки (строка #1 файла “стандартный ввод” File "", line 1, in <module>);
  2. Класс ошибки (деление на ноль — ZeroDivisionError);
  3. И даже подробную характеристику этого класса (division by zero)

Всё это так — “сервис исключений” (если его можно так назвать) в Python, позволяет эффективно бороться с ошибками после остановки программы. А может ли он помочь ещё до остановки программы, в процессе её выполнения?

Давайте рассмотрим пример. Предположим, в бесконечном цикле пользователь должен вводить значение делителя и получать: либо результат деления на введённое число (при правильном вводе), либо сообщение, что введённое число даст ошибку и просьбу ввести вместо него правильный вариант.

while True
    line = input('Please enter an integer: ')
    if line == 'end':
        break

    if line == '0':
        print('Zero gives an error! Please enter a different number!')
           continue

    result = 100 / int(line)
    print(result)

Программа будет работать до тех пор, пока пользователь не введёт end.

В этом примере учтена возможность ввода пользователем нулевого значения. Для этого каждое введённое значение прежде сравнивается с нулём. И если пользователь ввёл 0, то программа его не примет и попросит ввести другое значение.

Это действительно решает проблему. Но что будет, если число окажется дробным? А если пользователь по ошибке введёт букву и другой символ? В этом случае всё равно будет аварийное завершение программы. Мы конечно же может предусмотреть и эти два варианта ошибочного ввода и поставить точно такие же “заглушки” как и в случае ввода число 0. Но где гарантия, что не может возникнуть других ошибок ввода помимо этих трёх?

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

Исключения в Python. Конструкция try-except

Конструкция try-except — далеко не единственный способ использования исключений в Python. Более подробно этот вопрос рассмотриваются в следующих статьях:

Исключения в Python. Конструкция try-except-else

Исключения в Python. Конструкция try-except-finally

Исключения в Python. Перехват специфических ошибок

Исключения в Python. Операторы вызова исключений raise и assert

Читать дальше >>

Список тэгов

    Apps Script      Arrays Java Script      asynchronous code      asyncio      coroutine      Django      Dropdown List      Drop Shipping      Exceptions      GitHub      Google API      Google Apps Script      Google Docs      Google Drive      Google Sheets      multiprocessing      Parsing      Python      regex      Scraping      ssh      Test Driven Development (TDD)      threading      website monitoring      zip