В этой статье разбираем исключения (exception) и их обработку в Python 3. Познакомимся с операторами try, raise, assert и with.

Исключения (exceptions)

Исключения в программировании — это какая-то нетипичная ситуация (ошибка), которая приводит к завершению работы программы. Python 3 умеет обрабатывать исключения, поэтому программа не просто завершает свою работу, но и пишет сообщение о том, какая ошибка произошла.

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

Вот несколько примеров, когда срабатывает исключение.

Деление на ноль:

res = (10 / 0)

### Результат выполнения
Traceback (most recent call last):
  File "unit-6-2.py", line 18, in <module>
    res = (10 / 0)
           ~~~^~~
ZeroDivisionError: division by zero

Обращение к несуществующему индексу:

my_str = 'alex'
print(my_str[4])

### Результат выполнения
Traceback (most recent call last):
  File "unit-6-2.py", line 18, in <module>
    print(my_str[4])
          ~~~~~~^^^
IndexError: string index out of range

Попытка преобразовать строку в число:

int('строка')

### Результат выполнения
Traceback (most recent call last):
  File "unit-6-2.py", line 18, in <module>
    int('строка')
    ^^^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: 'строка'

Попытка прочитать закрытый файл:

f = open('mod_1.py', 'r')
f.close()
print(f.read()) 

### Результат выполнения
Traceback (most recent call last):
  File "unit-6-2.py", line 18, in <module>
    print(f.read())
          ^^^^^^^^
ValueError: I/O operation on closed file.

То есть вначале вам сообщается в каком файле и на какой строке произошла ошибка. А затем сообщается тип ошибки.

Оператор raise (создание исключений)

В Python 3 исключения возникают не сами собой. Ошибочная операция формирует исключение с помощью оператора raise. И мы можем вручную самостоятельно сгенерировать исключение с помощью этого оператора. После оператора raise пишется тип исключения (это класс). На каждый тип исключения есть свой класс.

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

То есть следующая строка кода приведёт к исключению деления на ноль:

raise ZeroDivisionError('Деление на ноль')

### Результат выполнения
Traceback (most recent call last):
  File "unit-6-2.py", line 27, in <module>
    raise ZeroDivisionError('Деление на ноль')
ZeroDivisionError: Деление на ноль

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

Давайте напишем тестовую функцию отправки принтера на печать с проверкой принтера:

def send_to_print(data):
    checkPrint = False
    if not checkPrint:
        raise Exception("Принтер не отвечает")
    print(data)

send_to_print("Привет!")

Так как принтер не отвечает (checkPrint = False), то сработает исключение написанное нами вручную (raise Exception("Принтер не отвечает")).

Пример с принтером взял из этого видео, там более подробно разбирается этот оператор.

Оператор try (обработка исключений)

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

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

Вот пример:

try:
    x = int(input("Введите первое число: "))
    y = int(input("Введите второе число: "))
    print(x / y) 
except ZeroDivisionError:
    print("Ошибка! На ноль делить нельзя.")
except ValueError:
    print("Ошибка! Вы ввели строку, а нужно было число.")
except:
    print("Что-то пошло не так...")
finally:
    print("Конец!")

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

Введите первое число: as
Ошибка! Вы ввели строку, а нужно было число.
Конец!
Введите первое число: 1
Введите второе число: 0
Ошибка! На ноль делить нельзя.
Конец!
Введите первое число: 6
Введите второе число: 3
2.0
Конец!

Оператор assert (тестирование кода)

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

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

Чтобы избежать ручной работы, вы можете написать тест с помощью оператора assert.

После assert пишется проверяемая функция с передаваемым параметром. Затем пишется знак равенства (==) и после него пишется значение, которое (по предположениям) должна функция возвратить. И если она возвратит что-то другое, то сработает исключение. Если программа не завершается с исключением, значит все тесты пройдены.

import string # Содержит наборы различных строк

def password_strength(my_str):
    digits = string.digits # Все цифры
    lowers = string.ascii_lowercase # Все символы
    uppers = lowers.upper() # Все большие символы
    
    # Если пароль меньше 8 символов
    if len(my_str) < 8:
        return 'Слишком слабый пароль'

    # Если пароль состоит полностью из цифр, или из маленьких букв, или из больших букв
    if all(e in digits for e in my_str) or all(e in lowers for e in my_str) or all(e in uppers for e in my_str):
        return 'Слабый пароль'

    # Если в пароле содержится хоть сколько-нибудь цифр и маленьких и больших букв     
    if any(e in digits for e in my_str) and any(e in lowers for e in my_str) and any(e in uppers for e in my_str):
        return 'Очень хороший пароль'
    
    # В остальных случаях (пароль состоящий хотя-бы из 2-ух наборов строк (маленькие и большие буквы, цифры и маленькие буквы, цифры и большие буквы)
    return 'Хороший пароль'

# Блок с тестами. Мы передаём в функцию разные строки и предполагаем результат её выполнения.
assert password_strength('') == 'Слишком слабый пароль'
assert password_strength('123') == 'Слишком слабый пароль'
assert password_strength('1234567') == 'Слишком слабый пароль'
assert password_strength('qazwsxe') == 'Слишком слабый пароль'
assert password_strength('QAZWSXE') == 'Слишком слабый пароль'
assert password_strength('12345678') == 'Слабый пароль'
assert password_strength('qazwsxed') == 'Слабый пароль'
assert password_strength('QAZWSXED') == 'Слабый пароль'
assert password_strength('1234qazw') == 'Хороший пароль'
assert password_strength('1234QAZW') == 'Хороший пароль'
assert password_strength('QAZwsxed') == 'Хороший пароль'
assert password_strength('123QAZws') == 'Очень хороший пароль'
assert password_strength('123wsxRFV') == 'Очень хороший пароль'

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

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

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

Пример с кодом взял из этого видео.

Оператор with (менеджер контекста)

Как программисты, мы можем работать с внешними ресурсами: базы данных, файлы, сессии на сайтах и так далее. Во всех этих случаях мы должны подключиться к объекту, поработать с ним и отключиться от него. Мы можем делать это самостоятельно, или использовать менеджер контекста with.

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

Демонстрация работы с файлом посредством оператора with:

with open('mod_1.py', 'r') as file:
    print(file.readlines(1))
    print(file.readlines(2))
    try:
        raise OSError
        file.close()
    except:
        print("Было перехвачено исключение, файл при этом не закрылся")
    print("Файл закрыт?", file.closed)
print("Файл закрыт?", file.closed)

### Результат выполнения
Первая строка из файла
Вторая строка из файла
Было перехвачено исключение, файл при этом не закрылся
Файл закрыт? False
Файл закрыт? True

В коде выше мы нигде не закрываем файл, но как только работа кода выходит из блока with, файл закрывается.

Итог

Мы познакомились с исключениями в Python 3. Научились их генерировать с помощью оператора raise, обрабатывать с помощью оператора try.

Также научились проводить тесты с помощью оператора assert и использовать менеджер контекста with.

Другие мои статьи по Python 3 можете посмотреть здесь.

Сводка
Python 3 - Исключения
Имя статьи
Python 3 - Исключения
Описание
В этой статье разбираем исключения (exception) и их обработку в Python 3. Познакомимся с операторами try, raise, assert и with.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *