
Тема № 3.2
Декораторы, ошибки и исключения
Лекция

Вспоминаем прошлый урок
- Отличия copy от deepcopy?
- Для чего предназначен модуль keyword?
- Какие дополнительные библиотеки были рассмотрены?
- Что такое «синтаксический» сахар и «с чем его едят»?

Содержание лекции
- Как понимать сообщения об ошибках в коде.
- Виды ошибок.
- Способы отладки программы.
- Понятие исключения, обработка исключений.
- Собственные исключения.

Типы ошибок
Разработка программы на любом языке программирования практически всегда бывает связана с возникновением различного рода ошибок, препятствующих получению желаемого результата.
Обычно выделяют следующие три категории ошибок:
- синтаксические – возникают из-за синтаксических погрешностей кода
- логические – проявляются вследствие логических неточностей в алгоритме
- ошибки времени выполнения, исключения – вызваны некорректными действиями пользователя или системы

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

Ошибки времени выполнения
Исключения представляют собой еще один вид ошибок, которые проявляются в зависимости от наличия обстоятельств, меняющих ход выполнения программы. Исключения являются ошибками времени выполнения, возникающие в процессе выполнения программы и связанные с некорректностью переданных в программу данных, недоступностью ресурсов и т.д.
Деление на ноль провоцирует исключительную ситуацию, которая приводит к аварийному завершению работы и выводу ошибки на экран. ZeroDivisionError — это название исключения, а division by zero — его краткое описание.
Если мы хотим, чтобы программа работала с широким диапазоном входных данных и внешних условий, то надо учитывать исключения, программа должна их верно обрабатывать.

Способы отладки программы
Цель отладки — убедиться, что ваш код не содержит ошибок и проблем, которые могут поставить под угрозу производительность программы.
Отладчики — это диагностические инструменты, которые позволяют вам видеть состояние выполнения и данные программы во время ее выполнения.
Как запустить отладчик
Если вы используете Python 3, используйте это:
python3 -m pdb example.py
Вы также можете запустить отладчик, выполнив в терминале следующую команду:
python -m pdb example.py
Не полагайтесь исключительно на print()

Удобная справочная таблица для команд pdb

Удобная справочная таблица для команд pdb

Дополнительные способы отладки в Python: IDE
Pdb — это отладчик командной строки, но многие IDE включают средства отладки, которые выглядят более привлекательными, а иногда и более простыми в использовании.
1. PyCharm
Вы можете начать сеанс отладки, запустив программу с прикрепленным к ней отладчиком. Его инструмент отладки имеет специальные панели для фреймов, переменных и часов, а также консоль, где он отображает входную и выходную информацию.
2. Отладчик Visual Studio
Вы можете отлаживать файл Python с помощью редактора кода, щелкнув «Выполнить» - «Начать отладку» или нажав F5, чтобы запустить текущий файл, в котором вы находитесь. Как и PyCharm, VS Code также поддерживает использование точек останова и пошагового выполнения

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

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

Обработка исключений
Если при выполнении кода в блоке try ошибка не возникла, то код обработки ошибки (исключения) в блоке except выполняться не будет. Если при выполнении кода в блоке try возникла ошибка, то выполнение кода trу блока прекращается, и выполняется код обработки ошибки (исключения) в блоке except . После этого управление передается следующей команде после конструкции try-except .
try:
num1 = int(input())
num2 = int(input())
print('Частное чисел равно', num1 / num2)
except:
print('Вы ввели некорректные данные!')
print('Работа программы завершена!')

Обработка нескольких исключений
В конструкции try-except после блока try указывается несколько ехсерt блоков, причем для каждого блока явно указывается тип ошибки (исключения), который обрабатывается в этом блоке.
try:
# контролируемый код
except тип _ ошибки _ 1:
# код обработки ошибки (исключения)
except тип _ ошибки _ 2:
# код обработки ошибки (исключения)
...
except тип _ ошибки _ n:
# код обработки ошибки (исключения)
#Пример
try:
num1 = int(input())
num2 = int(input())
print('Частное чисел равно', num1 / num2)
except ValueError:
print('Нужно было ввести числа!')
except ZeroDivisionError:
print('На ноль делить нельзя!')
print('Работа программы завершена!')

Основные типы исключений
- IndexError : возникает, когда индекс (например, для элемента списка) указан неправильно (выходит за границы допустимого диапазона)
- KeyError : возникает при неверно указанном ключе словаря
- NameError : возникает, если не удается найти переменную с некоторым названием
- SyntaxError : возникает при наличии в исходном коде синтаксических ошибок
- TypeError : возникает при несоответствии типов, когда для обработки требуется значение определенного типа, а передается значение другого типа
- FileNotFoundError : возникает при открытии несуществующего файла
- ValueError : возникает, когда в функцию передается аргумент с неподдерживаемым значением
- ZeroDivisionError : возникает при попытке выполнить деление на ноль

Необязательный блок else
try:
# контролируемый код
except тип _ ошибки _ 1:
# код обработки ошибки (исключения)
except тип _ ошибки _ 2:
# код обработки ошибки (исключения)
...
except тип _ ошибки _ n:
# код обработки ошибки (исключения)
else:
# код для случая, если ошибки (исключения) не было
Блок else размещается после последнего ехсерt блока и содержит программный код, который выполняется только в том случае , если при выполнении кода в trу блоке ошибок (исключений) не было.

Необязательный блок finally
try:
# контролируемый код
except тип _ ошибки _ 1:
# код обработки ошибки (исключения)
except тип _ ошибки _ 2:
# код обработки ошибки (исключения)
...
except тип _ ошибки _ n:
# код обработки ошибки (исключения)
finally :
# код, который выполняется всегда
Блок finally размещается после последнего ехсерt блока, либо после блока else, если он присутствует, и содержит программный код, который выполняется в любом случае , независимо от того, возникла ошибка (исключение) при выполнении кода trу блока или нет.
Блок finally особенно удобен при работе с файлами, которые нужно обязательно закрывать, независимо от того, произошла ошибка (исключение) или нет.

Определение декоратора
Декоратор — это функция, которая принимает другую функцию, расширяет ее поведение, не изменяя ее явно, и возвращает новую функцию.
Способ, который декорирует функцию say(), — многословен, приходится набирать имя функции несколько раз. Кроме того, декорирование скрывается под определением функции. Вместо этого Python позволяет использовать декораторы более простым способом с помощью символа @ .
def null _ decorator(func):
return func
def say():
print('Привет Мир!')
say = nul l_ decorator(say) # декорируем функцию
say()
def null _ decorator(func):
return func
@null _ decorator # декорируем функцию
def say():
print('Привет Мир!')
say()

Определение декоратора
def sample _ decorator(func): # определяем декоратор
def wrapper():
print('Начало функции')
func()
print('Конец функции')
return wrapper
def say():
print('Привет Мир!')
say = sample _ decorator(say) # декорируем функцию
say() # вызываем декорированную функцию
def sample _ decorator(func): # определяем декоратор
def wrapper():
print('Начало функции')
func()
print('Конец функции')
return wrapper
@sample _ decorator # декорируем функцию
def say():
print('Привет Мир!')
say()
Запись с @ является всего лишь синтаксическим сахаром для записи

Изменение поведения функции
Декоратор может менять поведение декорируемой функции.
Вместо того чтобы просто возвращать исходную функцию, как это делал null _ decorator() , декоратор uppercase _ decorator( ) определяет и возвращает новую функцию wrapper() . Функция wrapper(), являясь замыканием, имеет доступ к недекорированной функции func и может выполнять дополнительный код до и после вызова функции func.
def uppercase _ decorator(func):
def wrapper():
original _ result = func()
modified _ result = original _ result.upper()
return modified _ result
return wrapper
@uppercase _ decorator
def greet():
return 'Hello world!'
print(greet())

Применение нескольких декораторов
Мы можем без каких-либо проблем применять к функции несколько различных декораторов. Это накапливает их эффекты, и делает декораторы очень полезными на практике.
def bold(func):
def wrapper():
return '' + func() + ''
return wrapper
def italic(func):
def wrapper():
return '' + func() + ''
return wrapper
@bold
@italic
def greet():
return 'Hello world!'
print(greet())

Атрибуты __ name __ и __ doc __
- __ name __ — имя функции
- __ doc __ — строка документирования
def greet(name):
'''Функция приветствия пользователя.'''
return f'Hello {name}!'
print(greet. __ name __ )
print(greet. __ doc __ )
greet
Функция приветствия пользователя.
![Позиционные и именованные аргументы Для того чтобы разобраться с *args и **kwargs, нам нужно освоить концепции позиционных (positional) и именованных (keyword) аргументов. Оператор «звёздочка» Оператор * чаще всего ассоциируется у людей с операцией умножения, но в Python он имеет и другой смысл. Этот оператор позволяет «распаковывать» объекты, внутри которых хранятся некие элементы. Вот пример: a = [1,2,3] b = [*a,4,5,6] print(b) # [1,2,3,4,5,6]](http://fsd.mir-olymp.ru/html/2025/04/20/i_68048d033a7d8/img_phpoJpiEB_-3.2_22.jpg)
Позиционные и именованные аргументы
Для того чтобы разобраться с *args и **kwargs, нам нужно освоить концепции позиционных (positional) и именованных (keyword) аргументов.
Оператор «звёздочка»
Оператор * чаще всего ассоциируется у людей с операцией умножения, но в Python он имеет и другой смысл.
Этот оператор позволяет «распаковывать» объекты, внутри которых хранятся некие элементы. Вот пример:
a = [1,2,3]
b = [*a,4,5,6]
print(b) # [1,2,3,4,5,6]

Как пользоваться *args и **kwargs
*args — это сокращение от «arguments» (аргументы), а **kwargs — сокращение от «keyword arguments» (именованные аргументы).
Каждая из этих конструкций используется для распаковки аргументов соответствующего типа, позволяя вызывать функции со списком аргументов переменной длины. Например — создадим функцию, которая умеет выводить результаты, набранные учеником в тесте:
Главное — это символа * и два символа **

Как пользоваться *args и **kwargs
После того, как мы разобрались с *args, с пониманием **kwargs проблем быть уже не должно. Имя, опять же, значения не имеет. Главное — это два символа **. Благодаря им создаётся словарь, в котором содержатся именованные аргументы, переданные функции при её вызове.
«args и kwargs» — это всего лишь набор символов, которым принято обозначать аргументы. Самое главное тут — это оператор *.
Главное — это символа * и два символа **

Примечание
- Используйте общепринятые конструкции *args и **kwargs для захвата позиционных и именованных аргументов.
- Конструкцию **kwargs нельзя располагать до *args. Если это сделать — будет выдано сообщение об ошибке.
- Остерегайтесь конфликтов между именованными параметрами и **kwargs, в случаях, когда значение планируется передать как **kwarg-аргумент, но имя ключа этого значения совпадает с именем именованного параметра.
- Оператор *можно использовать не только в объявлениях функций, но и при их вызове.

Ваши
вопросы

Рефлексия
- Что было самым интересным на сегодняшнем занятии?
- Что было самым сложным?