Исключения (exceptions) - это события, которые возникают во время выполнения программы и нарушают нормальный ход выполнения команд. Когда в программе происходит ошибка, Python создает объект исключения, который содержит информацию об ошибке.
Python имеет множество встроенных типов исключений для различных ситуаций:
# Примеры различных типов исключений
# ValueError - неподходящее значение
try:
число = int("не число")
except ValueError as e:
print(f"Ошибка значения: {e}")
# IndexError - выход за пределы индекса
try:
список = [1, 2, 3]
элемент = список[10]
except IndexError as e:
print(f"Ошибка индекса: {e}")
# KeyError - отсутствующий ключ в словаре
try:
словарь = {"ключ": "значение"}
значение = словарь["несуществующий_ключ"]
except KeyError as e:
print(f"Ошибка ключа: {e}")
# ZeroDivisionError - деление на ноль
try:
результат = 10 / 0
except ZeroDivisionError as e:
print(f"Ошибка деления: {e}")
# TypeError - неподходящий тип данных
try:
результат = "строка" + 5
except TypeError as e:
print(f"Ошибка типа: {e}")
Основной способ обработки исключений в Python - это использование блоков try и except:
# Простая обработка исключений
try:
# Код, который может вызвать исключение
число = int(input("Введите число: "))
результат = 10 / число
print(f"Результат: {результат}")
except ValueError:
# Обработка конкретного типа исключения
print("Ошибка: введено не число!")
except ZeroDivisionError:
# Обработка другого типа исключения
print("Ошибка: деление на ноль!")
except Exception as e:
# Обработка всех остальных исключений
print(f"Произошла неизвестная ошибка: {e}")
else:
# Выполняется, если исключений не было
print("Операция выполнена успешно!")
finally:
# Выполняется всегда, независимо от исключений
print("Блок finally выполнен")
# Пример с обработкой нескольких исключений
def разделить_числа(делимое, делитель):
try:
результат = делимое / делитель
return результат
except ZeroDivisionError:
print("Ошибка: деление на ноль!")
return None
except TypeError:
print("Ошибка: неподходящий тип данных!")
return None
# Тестирование функции
print(разделить_числа(10, 2)) # 5.0
print(разделить_числа(10, 0)) # Ошибка: деление на ноль!
print(разделить_числа("10", 2)) # Ошибка: неподходящий тип данных!
Дополнительные блоки else и finally позволяют более точно управлять обработкой исключений:
# Пример с else и finally
def обработать_файл(имя_файла):
try:
# Попытка открыть файл
файл = open(имя_файла, 'r', encoding='utf-8')
except FileNotFoundError:
# Обработка исключения, если файл не найден
print(f"Ошибка: файл '{имя_файла}' не найден")
return None
except PermissionError:
# Обработка исключения, если нет прав доступа
print(f"Ошибка: нет прав доступа к файлу '{имя_файла}'")
return None
else:
# Выполняется только если исключений не было
print(f"Файл '{имя_файла}' успешно открыт")
содержимое = файл.read()
файл.close()
return содержимое
finally:
# Выполняется всегда, независимо от исключений
print("Операция завершена")
# Тестирование функции
print("Попытка открыть существующий файл:")
результат = обработать_файл("test.txt")
print(f"Результат: {результат}")
print("\nПопытка открыть несуществующий файл:")
результат = обработать_файл("несуществующий.txt")
print(f"Результат: {результат}")
Можно создавать собственные типы исключений, наследуясь от встроенных классов исключений:
# Создание собственного исключения
class НедостаточноСредствError(Exception):
def __init__(self, баланс, сумма):
self.баланс = баланс
self.сумма = сумма
super().__init__(f"Недостаточно средств: баланс {баланс}, требуется {сумма}")
class НедопустимыйВозрастError(ValueError):
def __init__(self, возраст):
self.возраст = возраст
super().__init__(f"Недопустимый возраст: {возраст}. Возраст должен быть положительным числом.")
# Использование собственных исключений
class БанковскийСчет:
def __init__(self, владелец, баланс=0):
self.владелец = владелец
self.баланс = баланс
def снять_деньги(self, сумма):
if сумма > self.баланс:
raise НедостаточноСредствError(self.баланс, сумма)
self.баланс -= сумма
return self.баланс
def пополнить_счет(self, сумма):
if сумма <= 0:
raise ValueError("Сумма пополнения должна быть положительной")
self.баланс += сумма
return self.баланс
def проверить_возраст(возраст):
if возраст < 0:
raise НедопустимыйВозрастError(возраст)
return True
# Тестирование собственных исключений
счет = БанковскийСчет("Иван", 100)
try:
счет.снять_деньги(150)
except НедостаточноСредствError as e:
print(f"Ошибка: {e}")
print(f"Текущий баланс: {e.баланс}")
print(f"Запрошенная сумма: {e.сумма}")
try:
проверить_возраст(-5)
except НедопустимыйВозрастError as e:
print(f"Ошибка возраста: {e}")
Python позволяет создавать цепочки исключений с помощью raise ... from ...:
# Пример цепочки исключений
def преобразовать_данные(данные):
try:
# Попытка преобразования данных
числа = [int(x) for x in данные.split(',')]
return числа
except ValueError as e:
# Перевыброс исключения с дополнительной информацией
raise TypeError(f"Невозможно преобразовать данные: {данные}") from e
def обработать_ввод(ввод_пользователя):
try:
return преобразовать_данные(ввод_пользователя)
except TypeError as e:
# Дополнительная обработка с сохранением цепочки исключений
print(f"Ошибка обработки ввода: {e}")
if e.__cause__:
print(f"Причина: {e.__cause__}")
raise # Перевыброс текущего исключения
# Тестирование цепочек исключений
try:
результат = обработать_ввод("1,2,три,4")
except TypeError as e:
print(f"Финальная ошибка: {e}")
if e.__cause__:
print(f"Первоначальная ошибка: {e.__cause__}")
Рассмотрим практические примеры использования обработки исключений:
class Калькулятор:
def сложить(self, a, b):
try:
return a + b
except TypeError:
raise TypeError("Невозможно сложить данные типы")
def разделить(self, a, b):
try:
if b == 0:
raise ZeroDivisionError("Деление на ноль")
return a / b
except TypeError:
raise TypeError("Невозможно разделить данные типы")
def вычислить(self, выражение):
try:
# Простой парсер выражений
части = выражение.split()
if len(части) != 3:
raise ValueError("Неверный формат выражения")
a = float(части[0])
оператор = части[1]
b = float(части[2])
if оператор == "+":
return self.сложить(a, b)
elif оператор == "/":
return self.разделить(a, b)
else:
raise ValueError(f"Неизвестный оператор: {оператор}")
except ValueError as e:
raise ValueError(f"Ошибка в выражении '{выражение}': {e}") from e
# Использование калькулятора с обработкой ошибок
калькулятор = Калькулятор()
выражения = [
"10 + 5",
"20 / 4",
"15 / 0", # Ошибка: деление на ноль
"abc + 5", # Ошибка: неверный формат числа
"10 * 3", # Ошибка: неизвестный оператор
"5 +" # Ошибка: неверный формат выражения
]
for выражение in выражения:
try:
результат = калькулятор.вычислить(выражение)
print(f"{выражение} = {результат}")
except (ValueError, ZeroDivisionError, TypeError) as e:
print(f"Ошибка в '{выражение}': {e}")
Попробуйте решить следующие задачи, применяя полученные знания об обработке исключений:
Создайте функцию безопасное_деление(делимое, делитель), которая безопасно выполняет деление двух чисел. Функция должна обрабатывать следующие исключения:
Функция должна возвращать результат деления или None, если произошла ошибка.
def безопасное_деление(делимое, делитель):
try:
return делимое / делитель
except TypeError:
print("Ошибка: оба аргумента должны быть числами")
return None
except ZeroDivisionError:
print("Ошибка: деление на ноль")
return None
# Тестирование
print(безопасное_деление(10, 2)) # 5.0
print(безопасное_деление(10, 0)) # Ошибка: деление на ноль
print(безопасное_деление("10", 2)) # Ошибка: оба аргумента должны быть числами
Создайте функцию проверить_возраст(возраст), которая проверяет, что возраст является положительным целым числом. Создайте собственное исключение НедопустимыйВозрастError. Функция должна:
class НедопустимыйВозрастError(Exception):
def __init__(self, возраст):
super().__init__(f"Недопустимый возраст: {возраст}. Возраст должен быть положительным числом.")
def проверить_возраст(возраст):
if not isinstance(возраст, (int, float)):
raise TypeError("Возраст должен быть числом")
if возраст < 0:
raise НедопустимыйВозрастError(возраст)
return True
# Тестирование
try:
print(проверить_возраст(25)) # True
print(проверить_возраст(-5)) # НедопустимыйВозрастError
except (TypeError, НедопустимыйВозрастError) as e:
print(f"Ошибка: {e}")
Создайте функцию прочитать_файл(имя_файла), которая безопасно читает содержимое файла. Функция должна обрабатывать следующие исключения:
Функция должна возвращать содержимое файла или None, если произошла ошибка.
def прочитать_файл(имя_файла):
try:
with open(имя_файла, 'r', encoding='utf-8') as файл:
return файл.read()
except FileNotFoundError:
print(f"Ошибка: файл '{имя_файла}' не найден")
return None
except PermissionError:
print(f"Ошибка: нет прав доступа к файлу '{имя_файла}'")
return None
except UnicodeDecodeError:
print(f"Ошибка: файл '{имя_файла}' имеет неподдерживаемую кодировку")
return None
# Тестирование
print(прочитать_файл("существующий.txt")) # Содержимое файла
print(прочитать_файл("несуществующий.txt")) # Ошибка: файл не найден