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

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


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


254
Часть I. Основы языка Python

__getattribute__(self, <Атрибут>)
— вызывается при обращении к любому атрибуту класса. Необходимо учитывать, что использование точечной нотации (для обращения к атрибуту класса) внутри этого метода приведет к зацикливанию. Чтобы избежать за- цикливания, следует вызвать метод
__getattribute__()
объекта object и внутри этого метода вернуть значение атрибута или возбудить исключение
AttributeError
: class MyClass: def __init__(self): self.i = 20 def __getattribute__(self, attr): print("Вызван метод __getattribute__()") return object.__getattribute__(self, attr) # Только так!!! c = MyClass() print(c.i) # Выведет: Вызван метод __getattribute__() 20

__setattr__(self, <Атрибут>, <Значение>)
— вызывается при попытке присваивания значения атрибуту экземпляра класса. Если внутри метода необходимо присвоить зна- чение атрибуту, следует использовать словарь
__dict__
, поскольку при применении то- чечной нотации метод
__setattr__()
будет вызван повторно, что приведет к зациклива- нию: class MyClass: def __setattr__(self, attr, value): print("Вызван метод __setattr__()") self.__dict__[attr] = value # Только так!!! c = MyClass() c.i = 10 # Выведет: Вызван метод __setattr__() print(c.i) # Выведет: 10

__delattr__(self, <Атрибут>)
— вызывается при удалении атрибута с помощью инст- рукции del <Экземпляр класса>.<Атрибут>
;

__len__(self)
— вызывается при использовании функции len()
, а также для проверки объекта на логическое значение при отсутствии метода
__bool__()
. Метод должен воз- вращать положительное целое число: class MyClass: def __len__(self): return 50 c = MyClass() print(len(c)) # Выведет: 50

__bool__(self)
— вызывается при использовании функции bool()
;

__int__(self)
— вызывается при преобразовании объекта в целое число с помощью функции int()
;

__float__(self)
— вызывается при преобразовании объекта в вещественное число с по- мощью функции float()
;

__complex__(self)
— вызывается при преобразовании объекта в комплексное число с помощью функции complex()
;

__round__(self, n)
— вызывается при использовании функции round()
;

__index__(self)
— вызывается при использовании функций bin()
, hex()
и oct()
;

Глава 13. Объектно-ориентированное программирование
255

__repr__(self)
и
__str__(self)
— служат для преобразования объекта в строку. Метод
__repr__()
вызывается при выводе в интерактивной оболочке, а также при использова- нии функции repr()
. Метод
__str__()
вызывается при выводе с помощью функции print()
, а также при использовании функции str()
. Если метод
__str__()
отсутствует, будет вызван метод
__repr__()
. В качестве значения методы
__repr__()
и
__str__()
должны возвращать строку: class MyClass: def __init__(self, m): self.msg = m def __repr__(self): return "Вызван метод __repr__() {0}".format(self.msg) def __str__(self): return "Вызван метод __str__() {0}".format(self.msg) c = MyClass("Значение") print(repr(c)) # Выведет: Вызван метод __repr__() Значение print(str(c)) # Выведет: Вызван метод __str__() Значение print(c) # Выведет: Вызван метод __str__() Значение

__hash__(self)
— этот метод следует переопределить, если экземпляр класса планиру- ется использовать в качестве ключа словаря или внутри множества: class MyClass: def __init__(self, y): self.x = y def __hash__(self): return hash(self.x) c = MyClass(10) d = {} d[c] = "Значение" print(d[c]) # Выведет: Значение
Классы поддерживают еще несколько специальных методов, которые применяются лишь в особых случаях и будут рассмотрены в главе 15.
13.6. Перегрузка операторов
Перегрузка операторов позволяет экземплярам классов участвовать в обычных операциях.
Чтобы перегрузить оператор, необходимо в классе определить метод со специальным на- званием. Для перегрузки математических операторов используются следующие методы:
 x + y
— сложение: x.__add__(y)
;
 y + x
— сложение (экземпляр класса справа): x.__radd__(y)
;
 x += y
— сложение и присваивание: x.__iadd__(y)
;
 x — y
— вычитание: x.__sub__(y)
;
 y — x
— вычитание (экземпляр класса справа): x.__rsub__(y)
;
 x -= y
— вычитание и присваивание: x.__isub__(y)
;
 x * y
— умножение: x.__mul__(y)
;
 y * x
— умножение (экземпляр класса справа): x.__rmul__(y)
;

256
Часть I. Основы языка Python
 x *= y
— умножение и присваивание: x.__imul__(y)
;
 x / y
— деление: x.__truediv__(y)
;
 y / x
— деление (экземпляр класса справа): x.__rtruediv__(y)
;
 x /= y
— деление и присваивание: x.__itruediv__(y)
;
 x // y
— деление с округлением вниз: x.__floordiv__(y)
;
 y // x
— деление с округлением вниз (экземпляр класса справа): x.__rfloordiv__(y)
;
 x //= y
— деление с округлением вниз и присваивание: x.__ifloordiv__(y)
;
 x % y
— остаток от деления: x.__mod__(y)
;
 y % x
— остаток от деления (экземпляр класса справа): x.__rmod__(y)
;
 x %= y
— остаток от деления и присваивание: x.__imod__(y)
;
 x ** y
— возведение в степень: x.__pow__(y)
;
 y ** x
— возведение в степень (экземпляр класса справа): x.__rpow__(y)
;
 x **= y
— возведение в степень и присваивание: x.__ipow__(y)
;

-x
— унарный минус: x.__neg__()
;

+x
— унарный плюс: x.__pos__()
;
 abs(x)
— абсолютное значение: x.__abs__()
Пример перегрузки математических операторов приведен в листинге 13.13.
Листинг 13.13. Пример перегрузки математических операторов class MyClass: def __init__(self, y): self.x = y def __add__(self, y): # Перегрузка оператора + print("Экземпляр слева") return self.x + y def __radd__(self, y): # Перегрузка оператора + print("Экземпляр справа") return self.x + y def __iadd__(self, y): # Перегрузка оператора += print("Сложение с присваиванием") self.x += y return self c = MyClass(50) print(c + 10) # Выведет: Экземпляр слева 60 print(20 + c) # Выведет: Экземпляр справа 70 c += 30 # Выведет: Сложение с присваиванием print(c.x) # Выведет: 80
Методы перегрузки двоичных операторов:


x
— двоичная инверсия: x.__invert__()
;
 x & y
— двоичное И: x.__and__(y)
;

Глава 13. Объектно-ориентированное программирование
257
 y & x
— двоичное И (экземпляр класса справа): x.__rand__(y)
;
 x &= y
— двоичное И и присваивание: x.__iand__(y)
;
 x | y
— двоичное ИЛИ: x.__or__(y)
;
 y | x
— двоичное ИЛИ (экземпляр класса справа): x.__ror__(y)
;
 x |= y
— двоичное ИЛИ и присваивание: x.__ior__(y)
;
 x ^ y
— двоичное исключающее ИЛИ: x.__xor__(y)
;
 y ^ x
— двоичное исключающее ИЛИ (экземпляр класса справа): x.__rxor__(y)
;
 x ^= y
— двоичное исключающее ИЛИ и присваивание: x.__ixor__(y)
;
 x << y
— сдвиг влево: x.__lshift__(y)
;
 y << x
— сдвиг влево (экземпляр класса справа): x.__rlshift__(y)
;
 x <<= y
— сдвиг влево и присваивание: x.__ilshift__(y)
;
 x >> y
— сдвиг вправо: x.__rshift__(y)
;
 y >> x
— сдвиг вправо (экземпляр класса справа): x.__rrshift__(y)
;
 x >>= y
— сдвиг вправо и присваивание: x.__irshift__(y)
Перегрузка операторов сравнения производится с помощью следующих методов:
 x == y
— равно: x.__eq__(y)
;
 x != y
— не равно: x.__ne__(y)
;
 x < y
— меньше: x.__lt__(y)
;
 x > y
— больше: x.__gt__(y)
;
 x <= y
— меньше или равно: x.__le__(y)
;
 x >= y
— больше или равно: x.__ge__(y)
;
 y in x
— проверка на вхождение: x.__contains__(y)
Пример перегрузки операторов сравнения приведен в листинге 13.14.
Листинг 13.14. Пример перегрузки операторов сравнения class MyClass: def __init__(self): self.x = 50 self.arr = [1, 2, 3, 4, 5] def __eq__(self, y): # Перегрузка оператора == return self.x == y def __contains__(self, y): # Перегрузка оператора in return y in self.arr c = MyClass() print("Равно" if c == 50 else "Не равно") # Выведет: Равно print("Равно" if c == 51 else "Не равно") # Выведет: Не равно print("Есть" if 5 in c else "Нет") # Выведет: Есть

258
Часть I. Основы языка Python
13.7. Статические методы и методы класса
Внутри класса можно создать метод, который будет доступен без создания экземпляра класса (статический метод). Для этого перед определением метода внутри класса следует указать декоратор
@staticmethod
. Вызов статического метода без создания экземпляра клас- са осуществляется следующим образом:
<Название класса>.<Название метода>(<Параметры>)
Кроме того, можно вызвать статический метод через экземпляр класса:
<Экземпляр класса>.<Название метода>(<Параметры>)
Пример использования статических методов приведен в листинге 13.15.
Листинг 13.15. Статические методы class MyClass:
@staticmethod def func1(x, y): # Статический метод return x + y def func2(self, x, y): # Обычный метод в классе return x + y def func3(self, x, y): return MyClass.func1(x, y) # Вызов из метода класса print(MyClass.func1(10, 20)) # Вызываем статический метод c = MyClass() print(c.func2(15, 6)) # Вызываем метод класса print(c.func1(50, 12)) # Вызываем статический метод
# через экземпляр класса print(c.func3(23, 5)) # Вызываем статический метод
# внутри класса
Обратите внимание на то, что в определении статического метода нет параметра self
. Это означает, что внутри статического метода нет доступа к атрибутам и методам экземпляра класса.
Методы класса создаются с помощью декоратора
@classmethod
. В качестве первого пара- метра в метод класса передается ссылка на класс. Вызов метода класса осуществляется сле- дующим образом:
<Название класса>.<Название метода>(<Параметры>)
Кроме того, можно вызвать метод класса через экземпляр класса:
<Экземпляр класса>.<Название метода>(<Параметры>)
Пример использования методов класса приведен в листинге 13.16.
Листинг 13.16. Методы класса class MyClass:
@classmethod def func(cls, x): # Метод класса print(cls, x)

Глава 13. Объектно-ориентированное программирование
259
MyClass.func(10) # Вызываем метод через название класса c = MyClass() c.func(50) # Вызываем метод класса через экземпляр
13.8. Абстрактные методы
Абстрактные методы содержат только определение метода без реализации. Предполагает- ся, что производный класс должен переопределить метод и реализовать его функциональ- ность. Чтобы такое предположение сделать более очевидным, часто внутри абстрактного метода возбуждают исключение (листинг 13.17).
Листинг 13.17. Абстрактные методы class Class1: def func(self, x): # Абстрактный метод
# Возбуждаем исключение с помощью raise raise NotImplementedError("Необходимо переопределить метод") class Class2(Class1): # Наследуем абстрактный метод def func(self, x): # Переопределяем метод print(x) class Class3(Class1): # Класс не переопределяет метод pass c2 = Class2() c2.func(50) # Выведет: 50 c3 = Class3() try: # Перехватываем исключения c3.func(50) # Ошибка. Метод func() не переопределен except NotImplementedError as msg: print(msg) # Выведет: Необходимо переопределить метод
В состав стандартной библиотеки входит модуль abc
. В этом модуле определен декоратор
@abstractmethod
, который позволяет указать, что метод, перед которым расположен декора- тор, является абстрактным. При попытке создать экземпляр производного класса, в котором не переопределен абстрактный метод, возбуждается исключение
TypeError
. Рассмотрим использование декоратора
@abstractmethod на примере (листинг 13.18).
Листинг 13.18. Использование декоратора @abstractmethod from abc import ABCMeta, abstractmethod class Class1(metaclass=ABCMeta):
@abstractmethod def func(self, x): # Абстрактный метод pass class Class2(Class1): # Наследуем абстрактный метод def func(self, x): # Переопределяем метод print(x)

260
Часть I. Основы языка Python class Class3(Class1): # Класс не переопределяет метод pass c2 = Class2() c2.func(50) # Выведет: 50 try: c3 = Class3() # Ошибка. Метод func() не переопределен c3.func(50) except TypeError as msg: print(msg) # Can't instantiate abstract class Class3
# with abstract methods func
Имеется возможность создания абстрактных статических методов и абстрактных методов класса, для чего необходимые декораторы указываются одновременно, друг за другом. Для примера объявим класс с абстрактными статическим методом и методом класса (лис- тинг 13.19).
Листинг 13.19. Абстрактный статический метод и абстрактный метод класса from abc import ABCMeta, abstractmethod class MyClass(metaclass=ABCMeta):
@staticmethod
@abstractmethod def static_func(self, x): # Абстрактный статический метод pass
@classmethod
@abstractmethod def class_func(self, x): # Абстрактный метод класса pass
13.9. Ограничение доступа к идентификаторам внутри класса
Все идентификаторы внутри класса в языке Python являются открытыми, т. е. доступны для непосредственного изменения. Для имитации частных идентификаторов можно воспользо- ваться методами
__getattr__()
,
__getattribute__()
и
__setattr__()
, которые перехваты- вают обращения к атрибутам класса. Кроме того, можно воспользоваться идентификатора- ми, названия которых начинаются с двух символов подчеркивания. Такие идентификаторы называются псевдочастными. Псевдочастные идентификаторы доступны лишь внутри класса, но никак не вне его. Тем не менее изменить идентификатор через экземпляр класса все равно можно, зная, каким образом искажается название идентификатора. Напри- мер, идентификатор
__privateVar внутри класса
Class1
будет доступен по имени
_Class1__ privateVar
. Как можно видеть, здесь перед идентификатором добавляется название класса с предваряющим символом подчеркивания. Приведем пример использования псевдочаст- ных идентификаторов (листинг 13.20).

Глава 13. Объектно-ориентированное программирование
261
Листинг 13.20. Псевдочастные идентификаторы class MyClass: def __init__(self, x): self.__privateVar = x def set_var(self, x): # Изменение значения self.__privateVar = x def get_var(self): # Получение значения return self.__privateVar c = MyClass(10) # Создаем экземпляр класса print(c.get_var()) # Выведет: 10 c.set_var(20) # Изменяем значение print(c.get_var()) # Выведет: 20 try: # Перехватываем ошибки print(c.__privateVar) # Ошибка!!! except AttributeError as msg: print(msg) # Выведет: 'MyClass' object has
# no attribute '__privateVar' c._MyClass__privateVar = 50 # Значение псевдочастных атрибутов
# все равно можно изменить print(c.get_var()) # Выведет: 50
Можно также ограничить перечень атрибутов, разрешенных для экземпляров класса. Для этого разрешенные атрибуты указываются внутри класса в атрибуте
__slots__
. В качестве значения атрибуту можно присвоить строку или список строк с названиями идентификато- ров. Если производится попытка обращения к атрибуту, не указанному в
__slots__
, возбу- ждается исключение
AttributeError
(листинг 13.21).
Листинг 13.21. Использование атрибута __slots__ class MyClass:
__slots__ = ["x", "y"] def __init__(self, a, b): self.x, self.y = a, b c = MyClass(1, 2) print(c.x, c.y) # Выведет: 1 2 c.x, c.y = 10, 20 # Изменяем значения атрибутов print(c.x, c.y) # Выведет: 10 20 try: # Перехватываем исключения c.z = 50 # Атрибут z не указан в __slots__,
# поэтому возбуждается исключение except AttributeError as msg: print(msg) # 'MyClass' object has no attribute 'z'
13.10. Свойства класса
Внутри класса можно создать идентификатор, через который в дальнейшем будут произво- диться операции получения и изменения значения какого-либо атрибута, а также его удале- ния. Создается такой идентификатор с помощью функции property()
. Формат функции:

262
Часть I. Основы языка Python
<Свойство> = property(<Чтение>[, <Запись>[, <Удаление>
[, <Строка документирования>]]])
В первых трех параметрах указывается ссылка на соответствующий метод класса. При по- пытке получить значение будет вызван метод, указанный в первом параметре. При опера- ции присваивания значения будет вызван метод, указанный во втором параметре, — этот метод должен принимать один параметр. В случае удаления атрибута вызывается метод, указанный в третьем параметре. Если в качестве какого-либо параметра задано значение
None
, то это означает, что соответствующий метод не поддерживается. Рассмотрим свойства класса на примере (листинг 13.22).
Листинг 13.22. Свойства класса class MyClass: def __init__(self, value): self.__var = value def get_var(self): # Чтение return self.__var def set_var(self, value): # Запись self.__var = value def del_var(self): # Удаление del self.__var v = property(get_var, set_var, del_var, "Строка документирования") c = MyClass(5) c.v = 35 # Вызывается метод set_var() print(c.v) # Вызывается метод get_var() del c.v # Вызывается метод del_var()
Python поддерживает альтернативный метод определения свойств — с помощью методов getter()
, setter()
и deleter()
, которые используются в декораторах. Соответствующий пример приведен в листинге 13.23.
Листинг 13.23. Методы getter(), setter() и deleter() class MyClass: def __init__(self, value): self.__var = value
@property def v(self): # Чтение return self.__var
@v.setter def v(self, value): # Запись self.__var = value
@v.deleter def v(self): # Удаление del self.__var c = MyClass(5) c.v = 35 # Запись print(c.v) # Чтение del c.v # Удаление

Глава 13. Объектно-ориентированное программирование
263
Имеется возможность определить абстрактное свойство — в этом случае все реализующие его методы должны быть переопределены в подклассе. Выполняется это с помощью знако- мого нам декоратора
1   ...   21   22   23   24   25   26   27   28   ...   83


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