ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 05.12.2023
Просмотров: 821
Скачиваний: 3
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
@abstractmethod из модуля abc
. Пример определения абстрактного свойства показан в листинге 13.24.
Листинг 13.24. Определение абстрактного свойства from abc import ABCMeta, abstractmethod class MyClass1(metaclass=ABCMeta): def __init__(self, value): self.__var = value
@property
@abstractmethod def v(self): # Чтение return self.__var
@v.setter
@abstractmethod def v(self, value): # Запись self.__var = value
@v.deleter
@abstractmethod def v(self): # Удаление del self.__var
13.11. Декораторы классов
В языке Python, помимо декораторов функций, поддерживаются декораторы классов, кото- рые позволяют изменить поведение самих классов. В качестве параметра декоратор прини- мает ссылку на объект класса, поведение которого необходимо изменить, и должен возвра- щать ссылку на тот же класс или какой-либо другой. Пример декорирования класса показан в листинге 13.25.
Листинг 13.25. Декоратор класса def deco(C): # Принимает объект класса print("Внутри декоратора") # Производит какие-то действия return C # Возвращает объект класса
@deco class MyClass: def __init__(self, value): self.v = value c = MyClass(5) print(c.v)
ГЛ А В А
14
Обработка исключений
Исключения — это извещения интерпретатора, возбуждаемые в случае возникновения ошибки в программном коде или при наступлении какого-либо события. Если в коде не предусмотрена обработка исключения, выполнение программы прерывается, и выводится сообщение об ошибке.
Существуют три типа ошибок в программе:
синтаксические — это ошибки в имени оператора или функции, отсутствие закрываю- щей или открывающей кавычек и т. д. — т. е. ошибки в синтаксисе языка. Как правило, интерпретатор предупредит о наличии ошибки, а программа не будет выполняться со- всем. Пример синтаксической ошибки:
>>> print("Нет завершающей кавычки!)
SyntaxError: EOL while scanning string literal
логические — это ошибки в логике программы, которые можно выявить только по ре- зультатам ее работы. Как правило, интерпретатор не предупреждает о наличии такой ошибки, и программа будет успешно выполняться, но результат ее выполнения окажется не тем, на который мы рассчитывали. Выявить и исправить такие ошибки весьма трудно;
ошибки времени выполнения — это ошибки, которые возникают во время работы про- граммы. Причиной являются события, не предусмотренные программистом. Классиче- ским примером служит деление на ноль:
>>> def test(x, y): return x / y
>>> test(4, 2) # Нормально
2.0
>>> test(4, 0) # Ошибка
Traceback (most recent call last):
File "
", line 1, in test(4, 0) # Ошибка
File "
", line 1, in test def test(x, y): return x / y
ZeroDivisionError: division by zero
Необходимо заметить, что в Python исключения возбуждаются не только при возникнове- нии ошибки, но и как уведомление о наступлении каких-либо событий. Например, метод index()
возбуждает исключение
ValueError
, если искомый фрагмент не входит в строку:
Глава 14. Обработка исключений
265
>>> "Строка".index("текст")
Traceback (most recent call last):
File "
", line 1, in
"Строка".index("текст")
ValueError: substring not found
14.1. Инструкция try...except...else...finally
Для обработки исключений предназначена инструкция try
. Формат инструкции: try:
<Блок, в котором перехватываются исключения>
[except [<Исключение1>[ as <Объект исключения>]]:
<Блок, выполняемый при возникновении исключения>
[... except [<ИсключениеN>[ as <Объект исключения>]]:
<Блок, выполняемый при возникновении исключения>]]
[else:
<Блок, выполняемый, если исключение не возникло>]
[finally:
<Блок, выполняемый в любом случае>]
Инструкции, в которых перехватываются исключения, должны быть расположены внутри блока try
. В блоке except в параметре
<Исключение1>
указывается класс обрабатываемого исключения. Например, обработать исключение, возникающее при делении на ноль, можно так, как показано в листинге 14.1.
Листинг 14.1. Обработка деления на ноль try: # Перехватываем исключения x = 1 / 0 # Ошибка: деление на 0 except ZeroDivisionError: # Указываем класс исключения print("Обработали деление на 0") x = 0 print(x) # Выведет: 0
Если в блоке try возникло исключение, управление передается блоку except
. В случае если исключение не соответствует указанному классу, управление передается следующему бло- ку except
. Если ни один блок except не соответствует исключению, то исключение «всплы- вает» к обработчику более высокого уровня. Если исключение в программе вообще нигде не обрабатывается, оно передается обработчику по умолчанию, который останавливает вы- полнение программы и выводит стандартную информацию об ошибке. Таким образом, в обработчике может быть несколько блоков except с разными классами исключений. Кро- ме того, один обработчик можно вложить в другой (листинг 14.2).
Листинг 14.2. Вложенные обработчики try: # Обрабатываем исключения try: # Вложенный обработчик x = 1 / 0 # Ошибка: деление на 0
266
Часть I. Основы языка Python except NameError: print("Неопределенный идентификатор") except IndexError: print("Несуществующий индекс") print("Выражение после вложенного обработчика") except ZeroDivisionError: print("Обработка деления на 0") x = 0 print(x) # Выведет: 0
В этом примере во вложенном обработчике не указано исключение
ZeroDivisionError
, поэтому исключение «всплывает» к обработчику более высокого уровня.
После обработки исключения управление передается инструкции, расположенной сразу после обработчика. В нашем примере управление будет передано инструкции, выводя- щей значение переменной x
, — print(x)
. Обратите внимание на то, что инструкция print("Выражение после вложенного обработчика")
выполнена не будет.
В инструкции except можно указать сразу несколько исключений, записав их через запятую внутри круглых скобок (листинг 14.3).
Листинг 14.3. Обработка нескольких исключений try: x = 1 / 0 except (NameError, IndexError, ZeroDivisionError):
# Обработка сразу нескольких исключений x = 0 print(x) # Выведет: 0
Получить информацию об обрабатываемом исключении можно через второй параметр в инструкции except
(листинг 14.4).
Листинг 14.4. Получение информации об исключении try: x = 1 / 0 # Ошибка деления на 0 except (NameError, IndexError, ZeroDivisionError) as err: print(err.__class__.__name__) # Название класса исключения print(err) # Текст сообщения об ошибке
Результат выполнения:
ZeroDivisionError division by zero
Для получения информации об исключении можно воспользоваться функцией exc_info()
из модуля sys
, которая возвращает кортеж из трех элементов: типа исключения, значения и объекта с трассировочной информацией. Преобразовать эти значения в удобочитаемый вид позволяет модуль traceback
. Пример использования функции exc_info()
и модуля traceback приведен в листинге 14.5.
Глава 14. Обработка исключений
267
Листинг 14.5. Пример использования функции exc_info() import sys, traceback try: x = 1 / 0 except ZeroDivisionError:
Type, Value, Trace = sys.exc_info() print("Type: ", Type) print("Value:", Value) print("Trace:", Trace) print("\n", "print_exception()".center(40, "-")) traceback.print_exception(Type, Value, Trace, limit=5, file=sys.stdout) print("\n", "print_tb()".center(40, "-")) traceback.print_tb(Trace, limit=1, file=sys.stdout) print("\n", "format_exception()".center(40, "-")) print(traceback.format_exception(Type, Value, Trace, limit=5)) print("\n", "format_exception_only()".center(40, "-")) print(traceback.format_exception_only(Type, Value))
Результат выполнения примера:
Type:
Value: division by zero
Trace:
-----------print_exception()------------
Traceback (most recent call last):
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.5.py", line 3, in x = 1 / 0
ZeroDivisionError: division by zero
---------------print_tb()---------------
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.5.py", line 3, in x = 1 / 0
-----------format_exception()-----------
['Traceback (most recent call last):\n', ' File
"D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.5.py", line 3, in\n x = 1 / 0\n', 'ZeroDivisionError: division by zero\n']
--------format_exception_only()---------
['ZeroDivisionError: division by zero\n']
Если в инструкции except не указан класс исключения, то такой блок будет перехватывать все исключения. На практике следует избегать пустых инструкций except
, т. к. можно пере- хватить исключение, которое является лишь сигналом системе, а не ошибкой. Пример пус- той инструкции except приведен в листинге 14.6.
268
Часть I. Основы языка Python
Листинг 14.6. Пример перехвата всех исключений try: x = 1 / 0 # Ошибка деления на 0 except: # Обработка всех исключений x = 0 print(x) # Выведет: 0
Если в обработчике присутствует блок else
, то инструкции внутри этого блока будут вы- полнены только при отсутствии ошибок. При необходимости выполнить какие-либо завер- шающие действия вне зависимости от того, возникло исключение или нет, следует восполь- зоваться блоком finally
. Для примера выведем последовательность выполнения блоков
(листинг 14.7).
Листинг 14.7. Блоки else и finally try: x = 10 / 2 # Нет ошибки
#x = 10 / 0 # Ошибка деления на 0 except ZeroDivisionError: print("Деление на 0") else: print("Блок else") finally: print("Блок finally")
Результат выполнения при отсутствии исключения:
Блок else
Блок finally
Последовательность выполнения блоков при наличии исключения будет другой:
Деление на 0
Блок finally
Необходимо заметить, что при наличии исключения и отсутствии блока except инструкции внутри блока finally будут выполнены, но исключение не будет обработано. Оно продол- жит «всплывание» к обработчику более высокого уровня. Если пользовательский обработ- чик отсутствует, управление передается обработчику по умолчанию, который прерывает выполнение программы и выводит сообщение об ошибке:
>>> try: x = 10 / 0 finally: print("Блок finally")
Блок finally
Traceback (most recent call last):
File "
", line 2, in x = 10 / 0
ZeroDivisionError: division by zero
В качестве примера переделаем нашу программу суммирования произвольного количества целых чисел, введенных пользователем (см. листинг 4.12), таким образом, чтобы при вводе строки вместо числа программа не завершалась с фатальной ошибкой (листинг 14.8).
Глава 14. Обработка исключений
269
Листинг 14.8. Суммирование неопределенного количества чисел
# -*- coding: utf-8 -*- print("Введите слово 'stop' для получения результата") summa = 0 while True: x = input("Введите число: ") if x == "stop": break # Выход из цикла try: x = int(x) # Преобразуем строку в число except ValueError: print("Необходимо ввести целое число!") else: summa += x print("Сумма чисел равна:", summa) input()
Процесс ввода значений и получения результата выглядит так (значения, введенные поль- зователем, выделены полужирным шрифтом):
Введите слово 'stop' для получения результата
Введите число:
10
Введите число: str
Необходимо ввести целое число!
Введите число:
-5
Введите число:
Необходимо ввести целое число!
Введите число: stop
Сумма чисел равна: 5 14.2. Инструкция with...as
Язык Python поддерживает протокол менеджеров контекста. Этот протокол гарантирует выполнение завершающих действий (например, закрытие файла) вне зависимости от того, произошло исключение внутри блока кода или нет.
Для работы с протоколом предназначена инструкция with...as
. Инструкция имеет сле- дующий формат: with <Выражение1>[ as <Переменная>][, ...,
<ВыражениеN>[ as <Переменная>]]:
<Блок, в котором перехватываем исключения>
Вначале вычисляется
<Выражение1>
, которое должно возвращать объект, поддерживающий протокол. Этот объект должен поддерживать два метода:
__enter__()
и
__exit__()
. Метод
__enter__()
вызывается после создания объекта. Значение, возвращаемое этим методом, присваивается переменной, указанной после ключевого слова as
. Если переменная не ука- зана, возвращаемое значение игнорируется. Формат метода
__enter__()
:
__enter__(self)
270
Часть I. Основы языка Python
Далее выполняются инструкции внутри тела инструкции with
. Если при выполнении воз- никло исключение, то управление передается методу
__exit__()
. Метод имеет следующий формат:
__exit__(self, <Тип исключения>, <Значение>, <Объект traceback>)
Значения, доступные через последние три параметра, полностью эквивалентны значениям, возвращаемым функцией exc_info()
из модуля sys
. Если исключение обработано, метод должен вернуть значение
True
, в противном случае —
False
. Если метод возвращает
False
, исключение передается вышестоящему обработчику.
Если при выполнении инструкций, расположенных внутри тела инструкции with
, исключе- ние не возникло, управление все равно передается методу
__exit__()
. В этом случае по- следние три параметра будут содержать значение
None
Рассмотрим последовательность выполнения протокола на примере (листинг 14.9).
Листинг 14.9. Протокол менеджеров контекста class MyClass: def __enter__(self): print("Вызван метод __enter__()") return self def __exit__(self, Type, Value, Trace): print("Вызван метод __exit__()") if Type is None: # Если исключение не возникло print("Исключение не возникло") else: # Если возникло исключение print("Value =", Value) return False # False — исключение не обработано
# True — исключение обработано print("Последовательность при отсутствии исключения:") with MyClass(): print("Блок внутри with") print("\nПоследовательность при наличии исключения:") with MyClass() as obj: print("Блок внутри with") raise TypeError("Исключение TypeError")
Результат выполнения:
Последовательность при отсутствии исключения:
Вызван метод __enter__()
Блок внутри with
Вызван метод __exit__()
Исключение не возникло
Последовательность при наличии исключения:
Вызван метод __enter__()
Блок внутри with
Вызван метод __exit__()
Value = Исключение TypeError
Traceback (most recent call last):
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
Глава 14. Обработка исключений
271
II/Примеры/14/14.9.py", line 19, in raise TypeError("Исключение TypeError")
TypeError: Исключение TypeError
Некоторые встроенные объекты, например файлы, поддерживают протокол по умолчанию.
Если в инструкции with указана функция open()
, то после выполнения инструкций внутри блока файл автоматически будет закрыт. Вот пример использования инструкции with
: with open("test.txt", "a", encoding="utf-8") as f: f.write("Строка\n") # Записываем строку в конец файла
Здесь файл test.txt открывается на дозапись данных. После выполнения функции open()
переменной f
будет присвоена ссылка на объект файла. С помощью этой переменной мы можем работать с файлом внутри тела инструкции with
. После выхода из блока вне зависи- мости от наличия исключения файл будет закрыт.
14.3. Классы встроенных исключений
Все встроенные исключения в языке Python представляют собой классы. Иерархия встроен- ных классов исключений схематично выглядит так:
BaseException
SystemExit
KeyboardInterrupt
GeneratorExit
Exception
StopIteration
ArithmeticError
FloatingPointError, OverflowError, ZeroDivisionError
AssertionError
AttributeError
BufferError
EOFError
ImportError
LookupError
IndexError, KeyError
MemoryError
NameError
UnboundLocalError
OSError
BlockingIOError
ChildProcessError
ConnectionError
BrokenPipeError, ConnectionAbortedError,
ConnectionRefusedError, ConnectionResetError
FileExistsError
FileNotFoundError
InterruptedError
IsADirectoryError
NotADirectoryError
PermissionError
272
Часть I. Основы языка Python
ProcessLookupError
TimeoutError
RecursionError
ReferenceError
RuntimeError
NotImplementedError
SyntaxError
IndentationError
TabError
SystemError
TypeError
ValueError
UnicodeError
UnicodeDecodeError, UnicodeEncodeError
UnicodeTranslateError
Warning
BytesWarning, DeprecationWarning, FutureWarning, ImportWarning,
PendingDeprecationWarning, ResourceWarning, RuntimeWarning,
SyntaxWarning, UnicodeWarning, UserWarning
Основное преимущество использования классов для обработки исключений заключается в возможности указания базового класса для перехвата всех исключений соответствующих производных классов. Например, для перехвата деления на ноль мы использовали класс
ZeroDivisionError
, но если вместо него указать базовый класс
ArithmeticError
, будут пере- хватываться исключения классов
FloatingPointError
,
OverflowError и
ZeroDivisionError
: try: x = 1 / 0 # Ошибка: деление на 0 except ArithmeticError: # Указываем базовый класс print("Обработали деление на 0")
Рассмотрим основные классы встроенных исключений:
BaseException
— является классом самого верхнего уровня и базовым для всех прочих классов исключений;
Exception
— базовый класс для большинства встроенных в Python исключений. Именно его, а не
BaseException
, необходимо наследовать при создании пользовательского класса исключения;
AssertionError
— возбуждается инструкцией assert
;
AttributeError
— попытка обращения к несуществующему атрибуту объекта;
EOFError
— возбуждается функцией input()
при достижении конца файла;
ImportError
— невозможно импортировать модуль или пакет;
. Пример определения абстрактного свойства показан в листинге 13.24.
Листинг 13.24. Определение абстрактного свойства from abc import ABCMeta, abstractmethod class MyClass1(metaclass=ABCMeta): def __init__(self, value): self.__var = value
@property
@abstractmethod def v(self): # Чтение return self.__var
@v.setter
@abstractmethod def v(self, value): # Запись self.__var = value
@v.deleter
@abstractmethod def v(self): # Удаление del self.__var
13.11. Декораторы классов
В языке Python, помимо декораторов функций, поддерживаются декораторы классов, кото- рые позволяют изменить поведение самих классов. В качестве параметра декоратор прини- мает ссылку на объект класса, поведение которого необходимо изменить, и должен возвра- щать ссылку на тот же класс или какой-либо другой. Пример декорирования класса показан в листинге 13.25.
Листинг 13.25. Декоратор класса def deco(C): # Принимает объект класса print("Внутри декоратора") # Производит какие-то действия return C # Возвращает объект класса
@deco class MyClass: def __init__(self, value): self.v = value c = MyClass(5) print(c.v)
ГЛ А В А
14
Обработка исключений
Исключения — это извещения интерпретатора, возбуждаемые в случае возникновения ошибки в программном коде или при наступлении какого-либо события. Если в коде не предусмотрена обработка исключения, выполнение программы прерывается, и выводится сообщение об ошибке.
Существуют три типа ошибок в программе:
синтаксические — это ошибки в имени оператора или функции, отсутствие закрываю- щей или открывающей кавычек и т. д. — т. е. ошибки в синтаксисе языка. Как правило, интерпретатор предупредит о наличии ошибки, а программа не будет выполняться со- всем. Пример синтаксической ошибки:
>>> print("Нет завершающей кавычки!)
SyntaxError: EOL while scanning string literal
логические — это ошибки в логике программы, которые можно выявить только по ре- зультатам ее работы. Как правило, интерпретатор не предупреждает о наличии такой ошибки, и программа будет успешно выполняться, но результат ее выполнения окажется не тем, на который мы рассчитывали. Выявить и исправить такие ошибки весьма трудно;
ошибки времени выполнения — это ошибки, которые возникают во время работы про- граммы. Причиной являются события, не предусмотренные программистом. Классиче- ским примером служит деление на ноль:
>>> def test(x, y): return x / y
>>> test(4, 2) # Нормально
2.0
>>> test(4, 0) # Ошибка
Traceback (most recent call last):
File "
", line 1, in
File "
", line 1, in test def test(x, y): return x / y
ZeroDivisionError: division by zero
Необходимо заметить, что в Python исключения возбуждаются не только при возникнове- нии ошибки, но и как уведомление о наступлении каких-либо событий. Например, метод index()
возбуждает исключение
ValueError
, если искомый фрагмент не входит в строку:
Глава 14. Обработка исключений
265
>>> "Строка".index("текст")
Traceback (most recent call last):
File "
", line 1, in
"Строка".index("текст")
ValueError: substring not found
14.1. Инструкция try...except...else...finally
Для обработки исключений предназначена инструкция try
. Формат инструкции: try:
<Блок, в котором перехватываются исключения>
[except [<Исключение1>[ as <Объект исключения>]]:
<Блок, выполняемый при возникновении исключения>
[... except [<ИсключениеN>[ as <Объект исключения>]]:
<Блок, выполняемый при возникновении исключения>]]
[else:
<Блок, выполняемый, если исключение не возникло>]
[finally:
<Блок, выполняемый в любом случае>]
Инструкции, в которых перехватываются исключения, должны быть расположены внутри блока try
. В блоке except в параметре
<Исключение1>
указывается класс обрабатываемого исключения. Например, обработать исключение, возникающее при делении на ноль, можно так, как показано в листинге 14.1.
Листинг 14.1. Обработка деления на ноль try: # Перехватываем исключения x = 1 / 0 # Ошибка: деление на 0 except ZeroDivisionError: # Указываем класс исключения print("Обработали деление на 0") x = 0 print(x) # Выведет: 0
Если в блоке try возникло исключение, управление передается блоку except
. В случае если исключение не соответствует указанному классу, управление передается следующему бло- ку except
. Если ни один блок except не соответствует исключению, то исключение «всплы- вает» к обработчику более высокого уровня. Если исключение в программе вообще нигде не обрабатывается, оно передается обработчику по умолчанию, который останавливает вы- полнение программы и выводит стандартную информацию об ошибке. Таким образом, в обработчике может быть несколько блоков except с разными классами исключений. Кро- ме того, один обработчик можно вложить в другой (листинг 14.2).
Листинг 14.2. Вложенные обработчики try: # Обрабатываем исключения try: # Вложенный обработчик x = 1 / 0 # Ошибка: деление на 0
266
Часть I. Основы языка Python except NameError: print("Неопределенный идентификатор") except IndexError: print("Несуществующий индекс") print("Выражение после вложенного обработчика") except ZeroDivisionError: print("Обработка деления на 0") x = 0 print(x) # Выведет: 0
В этом примере во вложенном обработчике не указано исключение
ZeroDivisionError
, поэтому исключение «всплывает» к обработчику более высокого уровня.
После обработки исключения управление передается инструкции, расположенной сразу после обработчика. В нашем примере управление будет передано инструкции, выводя- щей значение переменной x
, — print(x)
. Обратите внимание на то, что инструкция print("Выражение после вложенного обработчика")
выполнена не будет.
В инструкции except можно указать сразу несколько исключений, записав их через запятую внутри круглых скобок (листинг 14.3).
Листинг 14.3. Обработка нескольких исключений try: x = 1 / 0 except (NameError, IndexError, ZeroDivisionError):
# Обработка сразу нескольких исключений x = 0 print(x) # Выведет: 0
Получить информацию об обрабатываемом исключении можно через второй параметр в инструкции except
(листинг 14.4).
Листинг 14.4. Получение информации об исключении try: x = 1 / 0 # Ошибка деления на 0 except (NameError, IndexError, ZeroDivisionError) as err: print(err.__class__.__name__) # Название класса исключения print(err) # Текст сообщения об ошибке
Результат выполнения:
ZeroDivisionError division by zero
Для получения информации об исключении можно воспользоваться функцией exc_info()
из модуля sys
, которая возвращает кортеж из трех элементов: типа исключения, значения и объекта с трассировочной информацией. Преобразовать эти значения в удобочитаемый вид позволяет модуль traceback
. Пример использования функции exc_info()
и модуля traceback приведен в листинге 14.5.
Глава 14. Обработка исключений
267
Листинг 14.5. Пример использования функции exc_info() import sys, traceback try: x = 1 / 0 except ZeroDivisionError:
Type, Value, Trace = sys.exc_info() print("Type: ", Type) print("Value:", Value) print("Trace:", Trace) print("\n", "print_exception()".center(40, "-")) traceback.print_exception(Type, Value, Trace, limit=5, file=sys.stdout) print("\n", "print_tb()".center(40, "-")) traceback.print_tb(Trace, limit=1, file=sys.stdout) print("\n", "format_exception()".center(40, "-")) print(traceback.format_exception(Type, Value, Trace, limit=5)) print("\n", "format_exception_only()".center(40, "-")) print(traceback.format_exception_only(Type, Value))
Результат выполнения примера:
Type:
Value: division by zero
Trace:
-----------print_exception()------------
Traceback (most recent call last):
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.5.py", line 3, in
ZeroDivisionError: division by zero
---------------print_tb()---------------
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.5.py", line 3, in
-----------format_exception()-----------
['Traceback (most recent call last):\n', ' File
"D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.5.py", line 3, in
--------format_exception_only()---------
['ZeroDivisionError: division by zero\n']
Если в инструкции except не указан класс исключения, то такой блок будет перехватывать все исключения. На практике следует избегать пустых инструкций except
, т. к. можно пере- хватить исключение, которое является лишь сигналом системе, а не ошибкой. Пример пус- той инструкции except приведен в листинге 14.6.
268
Часть I. Основы языка Python
Листинг 14.6. Пример перехвата всех исключений try: x = 1 / 0 # Ошибка деления на 0 except: # Обработка всех исключений x = 0 print(x) # Выведет: 0
Если в обработчике присутствует блок else
, то инструкции внутри этого блока будут вы- полнены только при отсутствии ошибок. При необходимости выполнить какие-либо завер- шающие действия вне зависимости от того, возникло исключение или нет, следует восполь- зоваться блоком finally
. Для примера выведем последовательность выполнения блоков
(листинг 14.7).
Листинг 14.7. Блоки else и finally try: x = 10 / 2 # Нет ошибки
#x = 10 / 0 # Ошибка деления на 0 except ZeroDivisionError: print("Деление на 0") else: print("Блок else") finally: print("Блок finally")
Результат выполнения при отсутствии исключения:
Блок else
Блок finally
Последовательность выполнения блоков при наличии исключения будет другой:
Деление на 0
Блок finally
Необходимо заметить, что при наличии исключения и отсутствии блока except инструкции внутри блока finally будут выполнены, но исключение не будет обработано. Оно продол- жит «всплывание» к обработчику более высокого уровня. Если пользовательский обработ- чик отсутствует, управление передается обработчику по умолчанию, который прерывает выполнение программы и выводит сообщение об ошибке:
>>> try: x = 10 / 0 finally: print("Блок finally")
Блок finally
Traceback (most recent call last):
File "
", line 2, in
ZeroDivisionError: division by zero
В качестве примера переделаем нашу программу суммирования произвольного количества целых чисел, введенных пользователем (см. листинг 4.12), таким образом, чтобы при вводе строки вместо числа программа не завершалась с фатальной ошибкой (листинг 14.8).
Глава 14. Обработка исключений
269
Листинг 14.8. Суммирование неопределенного количества чисел
# -*- coding: utf-8 -*- print("Введите слово 'stop' для получения результата") summa = 0 while True: x = input("Введите число: ") if x == "stop": break # Выход из цикла try: x = int(x) # Преобразуем строку в число except ValueError: print("Необходимо ввести целое число!") else: summa += x print("Сумма чисел равна:", summa) input()
Процесс ввода значений и получения результата выглядит так (значения, введенные поль- зователем, выделены полужирным шрифтом):
Введите слово 'stop' для получения результата
Введите число:
10
Введите число: str
Необходимо ввести целое число!
Введите число:
-5
Введите число:
Необходимо ввести целое число!
Введите число: stop
Сумма чисел равна: 5 14.2. Инструкция with...as
Язык Python поддерживает протокол менеджеров контекста. Этот протокол гарантирует выполнение завершающих действий (например, закрытие файла) вне зависимости от того, произошло исключение внутри блока кода или нет.
Для работы с протоколом предназначена инструкция with...as
. Инструкция имеет сле- дующий формат: with <Выражение1>[ as <Переменная>][, ...,
<ВыражениеN>[ as <Переменная>]]:
<Блок, в котором перехватываем исключения>
Вначале вычисляется
<Выражение1>
, которое должно возвращать объект, поддерживающий протокол. Этот объект должен поддерживать два метода:
__enter__()
и
__exit__()
. Метод
__enter__()
вызывается после создания объекта. Значение, возвращаемое этим методом, присваивается переменной, указанной после ключевого слова as
. Если переменная не ука- зана, возвращаемое значение игнорируется. Формат метода
__enter__()
:
__enter__(self)
270
Часть I. Основы языка Python
Далее выполняются инструкции внутри тела инструкции with
. Если при выполнении воз- никло исключение, то управление передается методу
__exit__()
. Метод имеет следующий формат:
__exit__(self, <Тип исключения>, <Значение>, <Объект traceback>)
Значения, доступные через последние три параметра, полностью эквивалентны значениям, возвращаемым функцией exc_info()
из модуля sys
. Если исключение обработано, метод должен вернуть значение
True
, в противном случае —
False
. Если метод возвращает
False
, исключение передается вышестоящему обработчику.
Если при выполнении инструкций, расположенных внутри тела инструкции with
, исключе- ние не возникло, управление все равно передается методу
__exit__()
. В этом случае по- следние три параметра будут содержать значение
None
Рассмотрим последовательность выполнения протокола на примере (листинг 14.9).
Листинг 14.9. Протокол менеджеров контекста class MyClass: def __enter__(self): print("Вызван метод __enter__()") return self def __exit__(self, Type, Value, Trace): print("Вызван метод __exit__()") if Type is None: # Если исключение не возникло print("Исключение не возникло") else: # Если возникло исключение print("Value =", Value) return False # False — исключение не обработано
# True — исключение обработано print("Последовательность при отсутствии исключения:") with MyClass(): print("Блок внутри with") print("\nПоследовательность при наличии исключения:") with MyClass() as obj: print("Блок внутри with") raise TypeError("Исключение TypeError")
Результат выполнения:
Последовательность при отсутствии исключения:
Вызван метод __enter__()
Блок внутри with
Вызван метод __exit__()
Исключение не возникло
Последовательность при наличии исключения:
Вызван метод __enter__()
Блок внутри with
Вызван метод __exit__()
Value = Исключение TypeError
Traceback (most recent call last):
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
Глава 14. Обработка исключений
271
II/Примеры/14/14.9.py", line 19, in
TypeError: Исключение TypeError
Некоторые встроенные объекты, например файлы, поддерживают протокол по умолчанию.
Если в инструкции with указана функция open()
, то после выполнения инструкций внутри блока файл автоматически будет закрыт. Вот пример использования инструкции with
: with open("test.txt", "a", encoding="utf-8") as f: f.write("Строка\n") # Записываем строку в конец файла
Здесь файл test.txt открывается на дозапись данных. После выполнения функции open()
переменной f
будет присвоена ссылка на объект файла. С помощью этой переменной мы можем работать с файлом внутри тела инструкции with
. После выхода из блока вне зависи- мости от наличия исключения файл будет закрыт.
14.3. Классы встроенных исключений
Все встроенные исключения в языке Python представляют собой классы. Иерархия встроен- ных классов исключений схематично выглядит так:
BaseException
SystemExit
KeyboardInterrupt
GeneratorExit
Exception
StopIteration
ArithmeticError
FloatingPointError, OverflowError, ZeroDivisionError
AssertionError
AttributeError
BufferError
EOFError
ImportError
LookupError
IndexError, KeyError
MemoryError
NameError
UnboundLocalError
OSError
BlockingIOError
ChildProcessError
ConnectionError
BrokenPipeError, ConnectionAbortedError,
ConnectionRefusedError, ConnectionResetError
FileExistsError
FileNotFoundError
InterruptedError
IsADirectoryError
NotADirectoryError
PermissionError
272
Часть I. Основы языка Python
ProcessLookupError
TimeoutError
RecursionError
ReferenceError
RuntimeError
NotImplementedError
SyntaxError
IndentationError
TabError
SystemError
TypeError
ValueError
UnicodeError
UnicodeDecodeError, UnicodeEncodeError
UnicodeTranslateError
Warning
BytesWarning, DeprecationWarning, FutureWarning, ImportWarning,
PendingDeprecationWarning, ResourceWarning, RuntimeWarning,
SyntaxWarning, UnicodeWarning, UserWarning
Основное преимущество использования классов для обработки исключений заключается в возможности указания базового класса для перехвата всех исключений соответствующих производных классов. Например, для перехвата деления на ноль мы использовали класс
ZeroDivisionError
, но если вместо него указать базовый класс
ArithmeticError
, будут пере- хватываться исключения классов
FloatingPointError
,
OverflowError и
ZeroDivisionError
: try: x = 1 / 0 # Ошибка: деление на 0 except ArithmeticError: # Указываем базовый класс print("Обработали деление на 0")
Рассмотрим основные классы встроенных исключений:
BaseException
— является классом самого верхнего уровня и базовым для всех прочих классов исключений;
Exception
— базовый класс для большинства встроенных в Python исключений. Именно его, а не
BaseException
, необходимо наследовать при создании пользовательского класса исключения;
AssertionError
— возбуждается инструкцией assert
;
AttributeError
— попытка обращения к несуществующему атрибуту объекта;
EOFError
— возбуждается функцией input()
при достижении конца файла;
ImportError
— невозможно импортировать модуль или пакет;
1 ... 22 23 24 25 26 27 28 29 ... 83