Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Современные средства разработки программного обеспечения. Учебное пособие
.pdf
71
Фреймворки обычно сложнее библиотек. Кроме того, что фреймворки, как и
библиотеки содержат модули и пакеты, фреймворки содержат базовый поток и архитектуру приложения.
6.2 Обработка исключений
Исключение – это нежелательное событие / ошибка в программе, которая нару-
шает поток выполнения инструкций в программе и останавливает выполнение программы, если исключение не обрабатывается самой программой.
Даже если программа синтаксически корректна и обрабатывает все ограничения, могут быть ситуации, когда во время выполнения программы может возникнуть
ошибка, не зависящая от логики программы. Например, если во время операции чтения файла в python файл не найден, программа может столкнуться с ошибкой и преждевременно завершить выполнение.
В таких случаях программа должна вести себя более надежно и плавно справляться с ситуацией. Это можно сделать с помощью обработки ошибок try-except в
python.
В python есть несколько предопределенных исключений такие
как ZeroDivisionError, KeyError, NameError, IndexError и т.д. Также в python можно
объявлять пользовательские исключения.
Для обработки исключений, сгенерированных программой, в python используют блоки try except и finally.
# код с возможными исключениями
# необязательный блок
# Обработка исключения
#(если произойдет какое-либо исключение)
# код выполняется, если нет исключения
# код, вне зависимости от появления исключения

72
#(всегда выполняется)
– В try блоке есть код, который необходимо выполнить, который может генерировать ошибки / исключения.
– В except блоке содержится код для обработки ошибок / исключений, сгенерированных в блоке try.
– Код в блоке finally всегда выполняется, независимо от того, сгенерировал ли
блок try исключение или нет, блок finally будет выполнен.
Пример. Обработка ошибки деления на нуль. Вариант без ошибок.
x = 1
try:
print(5/x)
except ZeroDivisionError:
print("Это блок except! Произошла ошибка!")
else:
print("Это блок else! Ошибок нет!")
finally:
print("Это блок finally!")
print("Этот оператор выполнен после оператора try!")
Результат:
# 5.0
# Это блок except! Произошла ошибка!
# Это блок else! Ошибок нет!
# Это блок finally!")
# Этот оператор выполнен после оператора try!")
Блок finally часто используется, в программах, где выполняется обработка файлов или используются сетевые подключения, необходимо, чтобы программа завершала подключения или закрывала файл перед завершением.
В try-except в python, можно применять пользовательские исключения, группи-
ровать исключения и разные исключения обрабатывать по-разному.
Чтобы обрабатывать каждое исключение по-разному, можно предоставить аргу-
менты блокам исключений. Когда генерируется исключение того же типа, что и у аргумента, будет выполнен код в конкретном блоке.

73
Пример.
try:
number1 = int(input("Введите первое число: "))
number2 = int(input("Введите второе число: "))
print("Результат деления:", number1/number2)
except ValueError:
print("Преобразование прошло неудачно")
except ZeroDivisionError:
print("Попытка деления числа на ноль")
except Exception:
print("Общее исключение")
print("Завершение программы")
Группировка исключений
Можно обрабатывать несколько исключений одновременно, объединяя стандартные исключения.
Пример.
try:
with open('file.txt') as f:
content = f.readlines()
print(content)
except (IOError, NameError) as e:
print(str(e))
Пользовательские исключения
Множество исключений Python, которые поставляются со стандартной библиотекой удовлетворят почти все потребности программиста в обработке любых ошибок
или исключений.
Если же нужна расширенная функциональность, можно определить пользовательские исключения [25].
Пользовательские исключения определяют путем создания классов, которые
наследуются от класса Exception, а затем создают исключения с использованием клю-
чевого слова raise во время выполнения программы.
Пример. Создание пользовательского исключения.

74
class NameForError(Exception):
def __init__(self, message):
self.message = message
def infoNameForError(self):
print("info about of NameForError!")
В приведенном примере создан новый класс, унаследованный от класса
Exception и расширен.
Теперь можно обрабатывать это исключение как любой другой объект.
Пример. Обработка пользовательского исключения.
try:
raise NameForError("This is a error NameForError")
except NameForError as e:
e.infoNameForError()
Здесь сгенерировано исключение NameForError, затем оно перехвачено и дано
ему псевдоним e, и через псевдоним получен доступ к методу infoNameForError(),
который встроен в созданный класс NameForError.
6.3 Ведение журнала в Python
Когда в приложении что-то идет не так, отлаживать становится проще, если
знать источник ошибки. Когда возникает исключение, можно записать необходимую
информацию, чтобы отследить проблему. Python предоставляет простую и мощную
библиотеку ведения журнала.
Пример. Ведение журнала в Python при обработке исключений.
import logging
try:
logging.info('Trying to open the file')
filePointer = open('file.txt','r')
try:
logging.info('Trying to read the file content')
content = filePointer.readline()

75
print(content)
finally:
filePointer.close()
except IOError as e:
logging.error('Error occurred ' + str(e))
Как видно из приведенного выше кода, сначала нужно импортировать Python
библиотеку logging, а затем инициализировать регистратор (логер) с именем файла
журнала и уровнем ведения журнала. В примере установили уровень ведения журнала
равным info, поэтому любое сообщение, имеющее этот уровень, info будет регистрироваться.
Существует пять уровней протоколирования:
import logging
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical message
Получение трассировки стека
В приведенном выше коде был один программный файл, поэтому было проще
выяснить, где произошла ошибка. Но в случае, когда задействовано несколько программных файлов в поиске источника ошибки помогает получение трассировки стека
ошибки. Трассировка стека исключения может быть протоколирована:
import logging
# initialize the log settings
logging.basicConfig(filename = 'app.log', level = logging.INFO)
try:
filePointer = open('appFile','r')
try:
content = filePointer.readline()

76
finally:
filePointer.close()
except IOError as e:
logging.exception(str(e))
Если попытаться запустить вышеуказанную программу, при возникновении исключения в файле журнала будет зарегистрирована следующая ошибка:
ERROR:root:[Errno 2] No such file or directory: 'appFile'
Traceback (most recent call last):
File "readFile.py", line 7, in <module>
filePointer = open('appFile','r')
IOError: [Errno 2] No such file or directory: 'appFile'
Модули ведения журнала в Python
Стандартная библиотека Python содержит следующие модули для поддержки ведения журнала:
– Модуль logging: имеет основной клиентский API.
– Модуль logging.config: у него есть API, который позволяет настраивать кли-
ента.
– Модуль logging.handlers: предлагает различные обработчики, которые обра-
батывают и хранят записи журнала по-разному.
Все вместе эти модули называются библиотекой ведения журнала Python.
Настройка
Настроить корневой регистратор можно помощью basicConfig(**kwargs).
Наиболее распространенными параметрами являются уровень, формат и имя файла.
Когда есть очень простой скрипт и нужен очень простой механизм ведения журнала для сбора некоторой информации о времени выполнения, лучше всего подходит
Python basicConfig со значениями по умолчанию.
Пример. Код для создания корневого регистратора для записи журнала в консоль:
import logging
logging.basicConfig()
logging.warning("Ошибка аутентификации: %s", "неверное имя пользо-
вателя или пароль")

77
При запуске приведенного выше кода будет выведено в консоли:
WARNING: root: Ошибка аутентификации: неверное имя пользователя или
пароль.
Он указывает серьезность записи журнала, иерархию регистратора, а также
фактическое сообщение журнала.
Обычно также фиксируют временную метку события журнала. Чтобы указать,
какие детали (см. доступные атрибуты на сайте [26]) нужно добавить в запись журнала, можно указать свой собственный формат ведения журнала при инициализации
корневого регистратора.
Например, приведенный ниже код:
FORMAT = '[{asctime}] [{name}] [{levelname}] - {message}'
logging.basicConfig(format=FORMAT, style="{")
logging.warning("Ошибка аутентификации: %s", "неверное имя пользо-
вателя или пароль")
С приведенным выше пользовательским форматированием будет получен результат:
[2021-03-25 21:37:09,158] [root] [WARNING] - Ошибка аутентификации:
неверное имя пользователя или пароль.
Чтобы записывать журнал в файл, а не распечатывать его в консоли (sys.stderr),
можно изменить параметр filename для метода basicConfig, после чего будет запись
журнала в указанный файл.
logging.basicConfig(filename="a.log", format=FORMAT, style="{")
Если параметр filename не пуст, то logging создаст объект FileHandler для обработки операций, связанных с вводом-выводом.
Логирование на уровне модуля
Модуль ведения журнала Python имеет иерархию логеров для предков и потомков для совместного использования конфигураций или передачи сообщений. В приведенных выше примерах корневой регистратор инициализируется при вызове
basicConfig.

78
Хорошей практикой является создание logger на уровне модуля, чтобы была
возможность по-разному обрабатывать сообщения журнала для каждого модуля и отслеживать, какой модуль сгенерировал журналы.
Ниже приведен пример создания регистратора уровня модуля с помощью метода getLogger :
logging = logger.getLogger('module.x')
logger.setLevel(logging.debug)
Также можно создать пользовательский форматировщик и указать обработчик,
который нужен для регистратора на уровне модуля. Ниже приведен пример создания
StreamHandler с пользовательским форматом ведения журнала:
FORMAT = '[{asctime}] [{name}] [{levelname}] - {message}'
formatter = logging.Formatter(FORMAT, style="{")
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(formatter)
logger.addHandler(ch)
Чтобы проверить, работает ли регистратор должным образом, можно регистрировать некоторые сообщения на разных уровнях, как показано ниже:
def log_func(msg):
logger.debug("Отладка сейчас: %s", msg)
logger.info("Только для вашей информации : %s", msg)
logger.warning("Это предупреждение: %s", msg)
logger.error("Происходит ошибка : %s", msg)
logger.critical("Приложение завершит работу с ошибкой : %s",
msg)
log_func("ololo")
После запуска появится результат, аналогичный приведенному ниже:
[2024-05-24 12:59:36,787] [module.x] [INFO] - Только для вашей ин-
формации : ololo
[2024-05-24 12:59:36,787] [module.x] [WARNING] - Это предупрежде-
ние: ololo
[2024-05-24 12:59:36,787] [module.x] [ERROR] - Происходит ошибка :
ololo

79
[2024-05-24 12:59:36,787] [module.x] [CRITICAL] - Приложение за-
вершит работу с ошибкой : ololo
Аналогично иерархии пакетов Python, при указании имени регистратора как
“module.x.y” будет создан дочерний регистратор в разделе “module.x”.
Передача дополнительной информации в LogRecord
При вызове logger для регистрации некоторого события автоматически создается экземпляр LogRecord для передачи обработчику журнала. Если нужна дополнительная информация для добавления в LogRecord, то можно использовать параметр
extra вместе с форматером.
d = {'clientip': '192.168.0.1'}
FORMAT = '[{clientip}] [{asctime}] [{name}] [{levelname}] -
{message}'
logging.basicConfig(format=FORMAT, style="{")
logging.warning('Проблема с протоколом: %s', 'сброс подключения',
extra=d)
Будет результат:
[192.168.0.1] [2024-05-24 13:05:13,830] [root] [WARNING] - Проблема
с протоколом: сброс подключения
Создание нескольких файлов журнала с помощью RotatingFileHandler
Когда размер файла достигает определенного предела, чтобы избежать слишком большого размера конкретного файла журнала необходимо переключиться на новый файл журнала. RotatingFileHandler из модуля logging.handlers предоставляет про-
стой способ ограничить максимальный размер файла и количество создаваемых резервных копий файлов.
Пример. Установка размера файла в 2 КБ и создания максимум 5 резервных
файлов для ведения журнала:
from logging.handlers import RotatingFileHandler
logger = logging.getLogger("Приложение для примера")
logger.setLevel(logging.DEBUG)
fh = RotatingFileHandler(filename="appl.log", maxBytes=2000, back-
upCount=5)
fh.setLevel(logging.DEBUG)

80
formatter = logging.Formatter('[{asctime}] - [{name}] - [{level-
name}] - {message}', style="{")
fh.setFormatter(formatter)
logger.addHandler(fh)
Когда файл appl.log увеличится до 2 КБ, новые журналы будут автоматически
перенаправлены в новый файл, а старый файл будет переименован в appl.log.X (пока
X не достигнет 5). Если установить значение backupCount равным 1, файл журнала
будет увеличиваться без изменения на новый файл.
Аналогичным образом вы можете использовать TimedRotatingFileHandler [27]
для поворота файлов журналов через определенные промежутки времени, например,
каждые X часов / дней и т.д.
6.4 Контрольные вопросы
1 Что является модулем в Python?
2 Что может содержать модуль в Python?
3 Что такое пакет в Python?
4 Каково различие между модулем и пакетом в Python?
5 Какие выделяют категории модулей в Python?
6 Как можно подключить несколько модулей в Python?
7 Как можно создать псевдоним в Python?
8 Что содержит библиотека Python?
9 Что содержат фреймворки в Python?
10 Что такое исключение в Python?
11 Приведите примеры предопределенных исключений в Python?
12 Какие блоки используют в Python для обработки исключений, сгенериро-
ванных программой?
13 Для чего используют блок finally в Python?
14 Как можно обрабатывать в Python несколько исключений одновременно?
15 Как определяют пользовательские исключения в Python?
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
