Главная страница

ээдд. Прохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен. Николай Прохоренок Владимир Дронов


Скачать 7.92 Mb.
НазваниеНиколай Прохоренок Владимир Дронов
Дата05.05.2023
Размер7.92 Mb.
Формат файлаpdf
Имя файлаПрохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен.pdf
ТипДокументы
#1111379
страница27 из 83
1   ...   23   24   25   26   27   28   29   30   ...   83

IndentationError
неправильно расставлены отступы в программе;

IndexError
— указанный индекс не существует в последовательности;

KeyError
— указанный ключ не существует в словаре;

KeyboardInterrupt
— нажата комбинация клавиш +;

MemoryError
— интерпретатору существенно не хватает оперативной памяти;

Глава 14. Обработка исключений
273

NameError
— попытка обращения к идентификатору до его определения;

NotImplementedError
— должно возбуждаться в абстрактных методах;

OSError
— базовый класс для всех исключений, возбуждаемых в ответ на возникновение ошибок в операционной системе (отсутствие запрошенного файла, недостаток места на диске и пр.);

OverflowError
— число, получившееся в результате выполнения арифметической опера- ции, слишком велико, чтобы Python смог его обработать;

RecursionError
— превышено максимальное количество проходов рекурсии;

RuntimeError
— неклассифицированная ошибка времени выполнения;

StopIteration
— возбуждается методом
__next__()
как сигнал об окончании итераций;

SyntaxError
— синтаксическая ошибка;

SystemError
— ошибка в самой программе интерпретатора Python;

TabError
— в исходном коде программы встретился символ табуляции, использование которого для создания отступов недопустимо;

TypeError
— тип объекта не соответствует ожидаемому;

UnboundLocalError
— внутри функции переменной присваивается значение после обра- щения к одноименной глобальной переменной;

UnicodeDecodeError
— ошибка преобразования последовательности байтов в строку;

UnicodeEncodeError
— ошибка преобразования строки в последовательность байтов;

UnicodeTranslationError
— ошибка преобразования строки в другую кодировку;

ValueError
— переданный параметр не соответствует ожидаемому значению;

ZeroDivisionError
— попытка деления на ноль.
14.4. Пользовательские исключения
Для возбуждения пользовательских исключений предназначены две инструкции: raise и
assert
Инструкция raise возбуждает заданное исключение. Она имеет несколько вариантов фор- мата: raise <Экземпляр класса> raise <Название класса> raise <Экземпляр или название класса> from <Объект исключения> raise
В первом варианте формата инструкции raise указывается экземпляр класса возбуждае- мого исключения. При создании экземпляра можно передать конструктору класса данные, которые станут доступны через второй параметр в инструкции except
. Приведем пример возбуждения встроенного исключения
ValueError
:
>>> raise ValueError("Описание исключения")
Traceback (most recent call last):
File "
", line 1, in raise ValueError("Описание исключения")
ValueError: Описание исключения

274
Часть I. Основы языка Python
Пример обработки этого исключения показан в листинге 14.10.
Листинг 14.10. Программное возбуждение исключения try: raise ValueError("Описание исключения") except ValueError as msg: print(msg) # Выведет: Описание исключения
В качестве исключения можно указать экземпляр пользовательского класса (листинг 14.11).
Листинг 14.11. Создание собственного исключения class MyError(Exception): def __init__(self, value): self.msg = value def __str__(self): return self.msg
# Обработка пользовательского исключения try: raise MyError("Описание исключения") except MyError as err: print(err) # Вызывается метод __str__() print(err.msg) # Обращение к атрибуту класса
# Повторно возбуждаем исключение raise MyError("Описание исключения")
Результат выполнения:
Описание исключения
Описание исключения
Traceback (most recent call last):
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.11.py", line 13, in raise MyError("Описание исключения")
MyError: Описание исключения
Класс
Exception поддерживает все необходимые методы для вывода сообщения об ошибке.
Поэтому в большинстве случаев достаточно создать пустой класс, который наследует класс
Exception
(листинг 14.12).
Листинг 14.12. Упрощенный способ создания собственного исключения class MyError(Exception): pass try: raise MyError("Описание исключения") except MyError as err: print(err) # Выведет: Описание исключения
Во втором варианте формата инструкции raise в первом параметре задается объект клас- са, а не экземпляр:

Глава 14. Обработка исключений
275 try: raise ValueError # Эквивалентно: raise ValueError() except ValueError: print("Сообщение об ошибке")
В третьем варианте формата инструкции raise в первом параметре задается экземпляр класса или просто название класса, а во втором параметре указывается объект исключения.
В этом случае объект исключения сохраняется в атрибуте
__cause__
. При обработке вло- женных исключений эти данные используются для вывода информации не только о послед- нем исключении, но и о первоначальном исключении. Пример этого варианта формата ин- струкции raise можно увидеть в листинге 14.13.
Листинг 14.13. Применение третьего варианта формата инструкции raise try: x = 1 / 0 except Exception as err: raise ValueError() from err
Результат выполнения:
Traceback (most recent call last):
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.13.py", line 2, in x = 1 / 0
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.13.py", line 4, in raise ValueError() from err
ValueError
Как видно из результата, мы получили информацию не только по исключению
ValueError
, но и по исключению
ZeroDivisionError
. Следует заметить, что при отсутствии инструкции from информация сохраняется неявным образом. Если убрать инструкцию from в предыду- щем примере, мы получим следующий результат:
Traceback (most recent call last):
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.13.py", line 2, in x = 1 / 0
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.13.py", line 4, in raise ValueError()
ValueError

276
Часть I. Основы языка Python
Четвертый вариант формата инструкции raise позволяет повторно возбудить последнее исключение и обычно применяется в коде, следующем за инструкцией except
. Пример это- го варианта показан в листинге 14.14.
Листинг 14.14. Применение четвертого варианта формата инструкции raise class MyError(Exception): pass try: raise MyError("Сообщение об ошибке") except MyError as err: print(err) raise # Повторно возбуждаем исключение
Результат выполнения:
Сообщение об ошибке
Traceback (most recent call last):
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/14/14.14.py", line 3, in raise MyError("Сообщение об ошибке")
MyError: Сообщение об ошибке
Инструкция assert возбуждает исключение
AssertionError
, если логическое выражение возвращает значение
False
. Инструкция имеет следующий формат: assert <Логическое выражение>[, <Данные>]
Инструкция assert эквивалентна следующему коду: if __debug__: if not <Логическое выражение>: raise AssertionError(<Данные>)
Если при запуске программы используется флаг
-O
, то переменная
__debug__
будет иметь ложное значение. Таким образом можно удалить все инструкции assert из байт-кода.
Пример использования инструкции assert представлен в листинге 14.15.
Листинг 14.15. Использование инструкции assert try: x = -3 assert x >= 0, "Сообщение об ошибке" except AssertionError as err: print(err) # Выведет: Сообщение об ошибке

ГЛ А В А
15
Итераторы, контейнеры и перечисления
Язык Python поддерживает средства для создания классов особого назначения: итераторов, контейнеров и перечислений.
Итераторы — это классы, генерирующие последовательности каких-либо значений. Такие классы мы можем задействовать, например, в циклах for
: class MyIterator: # Определяем класс-итератор it = MyIterator() # Создаем его экземпляр for v in it: # и используем в цикле for
Контейнеры — классы, которые могут выступать как последовательности (списки или кор- тежи) или отображения (словари). Мы можем обратиться к любому элементу экземпляра такого класса через его индекс или ключ: class MyList: # Определяем класс-список class MyDict: # Определяем класс-словарь lst, dct = MyList(), MyDict() # Используем их lst[0] = 1 dct["first"] = 578 print(lst[1]), print(dct["second"])
Перечисления — особые классы, представляющие наборы каких-либо именованных вели- чин. В этом смысле они аналогичны подобным типам данных, доступным в других языках программирования, — например, в C: from enum import Enum # Импортируем базовый класс Enum class Versions(Enum): # Определяем класс-перечисление
Python2.7 = "2.7"
Python3.6 = "3.6"
# Используем его if python_version == Versions.Python3.6:

278
Часть I. Основы языка Python
15.1. Итераторы
Для того чтобы превратить класс в итератор, нам следует переопределить в нем два специ- альных метода:

__iter__(self)
— говорит о том, что этот класс является итератором (поддерживает итерационный протокол, как говорят Python-программисты). Он должен возвращать сам экземпляр этого класса, а также при необходимости может выполнять всевозможные предустановки.
Если в классе одновременно определены методы
__iter__()
и
__getitem__()
(о нем будет рассказано позже), предпочтение отдается первому методу;

__next__(self)
— вызывается при выполнении каждой итерации и должен возвращать очередное значение из последовательности. Если последовательность закончилась, в этом методе следует возбудить исключение
StopIteration
, которое сообщит вызы- вающему коду об окончании итераций.
Для примера рассмотрим класс, хранящий строку и на каждой итерации возвращающий очередной ее символ, начиная с конца (листинг 15.1).
Листинг 15.1. Класс-итератор class ReverseString: def __init__(self, s): self.__s = s def __iter__(self): self.__i = 0 return self def __next__(self): if self.__i > len(self.__s) - 1: raise StopIteration else: a = self.__s[-self.__i - 1] self.__i = self.__i + 1 return a
Проверим его в действии:
>>> s = ReverseString("Python")
>>> for a in s: print(a, end="") nohtyP
Результат вполне ожидаем — строка, выведенная задом наперед.
Также мы можем переопределить специальный метод
__len()__
, который вернет количест- во элементов в последовательности, и, разумеется, специальные методы
__str()__
и
__repr()__
, возвращающие строковое представление итератора (все эти методы были рас- смотрены в главе 13).
Перепишем код нашего класса-итератора, добавив в него определение методов
__len()__
и
__str()__
(листинг 15.2 — часть кода опущена).

Глава 15. Итераторы, контейнеры и перечисления
279
Листинг 15.2. Расширенный класс-итератор class ReverseString: def __len__(self): return len(self.__s) def __str__(self): return self.__s[::-1]
Теперь мы можем получить длину последовательности, хранящейся в экземпляре класса
ReverseString
, и его строковое представление:
>>> s = ReverseString("Python")
>>> print(len(s))
6
>>> print(str(s)) nohtyP
15.2. Контейнеры
Python позволяет создать как контейнеры-последовательности, аналогичные спискам и кор- тежам, так и контейнеры-отображения, т. е. словари. Сейчас мы узнаем, как это делается.
15.2.1. Контейнеры-последовательности
Чтобы класс смог реализовать функциональность последовательности, нам следует переоп- ределить в нем следующие специальные методы:

__getitem__(self, <Индекс>)
— вызывается при извлечении элемента последовательно- сти по его индексу с помощью операции
<Экземпляр класса>[<Индекс>]
. Метод должен возвращать значение, расположенное по этому индексу. Если индекс не является целым числом или срезом, должно возбуждаться исключение
TypeError
, а если индекса как та- кового не существует, следует возбудить исключение
IndexError
;

__setitem__(self, <Индекс>, <Значение>)
— вызывается в случае присваивания нового значения элементу последовательности с заданным индексом (операция
<Экземпляр класса>[<Индекс>] = <Новое значение>
). Метод не должен возвращать результата.
В случае задания индекса недопустимого типа и отсутствия такого индекса в последова- тельности следует возбуждать те же исключения, что и в случае метода
__getitem__()
;

__delitem__(self, <Ключ>)
— вызывается в случае удаления элемента последовательно- сти с заданным индексом с помощью выражения del <Экземпляр класса>[<Ключ>]
Метод не должен возвращать результата. В случае задания индекса недопустимого типа и отсутствия такого индекса в последовательности следует возбуждать те же исключе- ния, что и в случае метода
__getitem__()
;

__contains__(self, <Значение>)
— вызывается при проверке существования заданного значения в последовательности с применением операторов in и not in
. Метод должен возвращать
True
, если такое значение есть, и
False
— в противном случае.
В классе-последовательности мы можем дополнительно реализовать функциональность итератора (см. разд. 15.1), переопределив специальные методы
__iter__()
,
__next__()
и
__len__()
. Чаще всего так и поступают.

280
Часть I. Основы языка Python
Мы уже давно знаем, что строки в Python являются неизменяемыми. Давайте же напишем класс
MutableString
, представляющий строку, которую можно изменять теми же способа- ми, что и список (листинг 15.3).
Листинг 15.3. Класс MutableString class MutableString: def __init__(self, s): self.__s = list(s)
# Реализуем функциональность итератора def __iter__(self): self.__i = 0 return self def __next__(self): if self.__i > len(self.__s) - 1: raise StopIteration else: a = self.__s[self.__i] self.__i = self.__i + 1 return a def __len__(self): return len(self.__s) def __str__(self): return "".join(self.__s)
# Определяем вспомогательный метод, который будет проверять
# корректность индекса def __iscorrectindex(self, i): if type(i) == int or type(i) == slice: if type(i) == int and i > self.__len__() - 1: raise IndexError else: raise TypeError
# Реализуем функциональность контейнера-списка def __getitem__(self, i): self.__iscorrectindex(i) return self.__s[i] def __setitem__(self, i, v): self.__iscorrectindex(i) self.__s[i] = v def __delitem__(self, i): self.__iscorrectindex(i) del self.__s[i] def __contains__(self, v): return v in self.__s

Глава 15. Итераторы, контейнеры и перечисления
281
Проверим свеженаписанный класс в действии:
>>> s = MutableString("Python")
>>> print(s[-1]) n
>>> s[0] = "J"
>>> print(s)
Jython
>>> del s[2:4]
>>> print(s)
Juon
Теперь проверим, как наш класс обрабатывает нештатные ситуации. Введем вот такой код, обращающийся к элементу с несуществующим индексом:
>>> s[9] = "u"
В ответ интерпретатор Python выдаст вполне ожидаемое сообщение об ошибке:
Traceback (most recent call last):
File "
", line 1, in s[9] = "u"
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/15/15.3.py", line 36, in __setitem__ self.__iscorrectindex(i)
File "D:/Data/Документы/Работа/Книги/Python 3 и PyQt 5 Разработка приложений
II/Примеры/15/15.3.py", line 27, in __iscorrectindex raise IndexError
IndexError
15.2.2. Контейнеры-словари
Класс, реализующий функциональность перечисления, должен переопределять уже знако- мые нам методы:
__getitem__()
,
__setitem__()
,
__delitem__()
и
__contains__()
. Разумеет- ся, при этом следует сделать поправку на то, что вместо индексов здесь будут использо- ваться ключи произвольного типа (как правило, строкового).
Давайте исключительно для практики напишем класс
Version
, который будет хранить но- мер версии интерпретатора Python, разбитый на части: старшая цифра, младшая цифра и подрелиз, при этом доступ к частям номера версии будет осуществляться по строковым ключам, как в обычном словаре Python (листинг 15.4). Ради простоты чтения кода функ- циональность итератора реализовывать не станем, а также заблокируем операцию удаления элемента словаря, возбудив в методе
__delitem__()
исключение
TypeError
Листинг 15.4. Класс Version class Version: def __init__(self, major, minor, sub): self.__major = major # Старшая цифра self.__minor = minor # Младшая цифра self.__sub = sub # Подверсия def __str__(self): return str(self.__major) + "." + str(self.__minor) + "." +
 str(self.__sub)

282
Часть I. Основы языка Python
# Реализуем функциональность словаря def __getitem__(self, k): if k == "major": return self.__major elif k == "minor": return self.__minor elif k == "sub": return self.__sub else: raise IndexError def __setitem__(self, k, v): if k == "major": self.__major = v elif k == "minor": self.__minor = v elif k == "sub": self.__sub = v else: raise IndexError def __delitem__(self, k): raise TypeError def __contains__(self, v): return v == "major" or v == "minor" or v == "sub"
Чтобы наш новый класс не бездельничал, дадим ему работу, введя такой код:
>>> v = Version(3, 6, 3)
>>> print(v["major"])
3
>>> v["sub"] = 4
>>> print(str(v))
3.6.4
Как видим, все работает как надо.
15.3. Перечисления
Перечисление — это определенный самим программистом набор каких-либо именованных значений. Обычно они применяются для того, чтобы дать понятные имена каким-либо зна- чениям, используемым в коде программы, — например, кодам ошибок, возвращаемых функциями Windows API.
1   ...   23   24   25   26   27   28   29   30   ...   83


написать администратору сайта