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

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


Скачать 7.92 Mb.
НазваниеНиколай Прохоренок Владимир Дронов
Дата05.05.2023
Размер7.92 Mb.
Формат файлаpdf
Имя файлаПрохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен.pdf
ТипДокументы
#1111379
страница24 из 83
1   ...   20   21   22   23   24   25   26   27   ...   83
<Экземпляр класса>.<Имя метода>([<Параметры>])
Обратите внимание на то, что при вызове метода не нужно передавать ссылку на экземпляр класса в качестве параметра, как это делается в определении метода внутри класса. Ссылку на экземпляр класса интерпретатор передает автоматически.
Обращение к атрибутам класса осуществляется аналогично:
<Экземпляр класса>.<Имя атрибута>
Определим класс
MyClass с атрибутом x
и методом print_x()
, выводящим значение этого атрибута, а затем создадим экземпляр класса и вызовем метод (листинг 13.2).
Листинг 13.2. Создание атрибута и метода class MyClass: def __init__(self): # Конструктор self.x = 10 # Атрибут экземпляра класса def print_x(self): # self — это ссылка на экземпляр класса print(self.x) # Выводим значение атрибута c = MyClass() # Создание экземпляра класса
# Вызываем метод print_x() c.print_x() # self не указывается при вызове метода print(c.x) # К атрибуту можно обратиться непосредственно
Для доступа к атрибутам и методам можно использовать и следующие функции:
 getattr()
— возвращает значение атрибута по его названию, заданному в виде строки.
С помощью этой функции можно сформировать имя атрибута динамически во время выполнения программы. Формат функции: getattr(<Объект>, <Атрибут>[, <Значение по умолчанию>])
Если указанный атрибут не найден, возбуждается исключение
AttributeError
. Чтобы избежать вывода сообщения об ошибке, в третьем параметре можно указать значение, которое будет возвращаться, если атрибут не существует;
 setattr()
— задает значение атрибута. Название атрибута указывается в виде строки.
Формат функции: setattr(<Объект>, <Атрибут>, <Значение>)

246
Часть I. Основы языка Python
Вторым параметром функции setattr()
можно передать имя несуществующего атрибу- та — в этом случае атрибут с указанным именем будет создан;
 delattr(<Объект>, <Атрибут>)
— удаляет атрибут, чье название указано в виде строки;
 hasattr(<Объект>, <Атрибут>)
— проверяет наличие указанного атрибута. Если атрибут существует, функция возвращает значение
True
Продемонстрируем работу функций на примере (листинг 13.3).
Листинг 13.3. Функции getattr(), setattr() и hasattr() class MyClass: def __init__(self): self.x = 10 def get_x(self): return self.x c = MyClass() # Создаем экземпляр класса print(getattr(c, "x")) # Выведет: 10 print(getattr(c, "get_x")()) # Выведет: 10 print(getattr(c, "y", 0)) # Выведет: 0, т. к. атрибут не найден setattr(c, "y", 20) # Создаем атрибут y print(getattr(c, "y", 0)) # Выведет: 20 delattr(c, "y") # Удаляем атрибут y print(getattr(c, "y", 0)) # Выведет: 0, т. к. атрибут не найден print(hasattr(c, "x")) # Выведет: True print(hasattr(c, "y")) # Выведет: False
Все атрибуты класса в языке Python являются открытыми (public), т. е. доступными для не- посредственного изменения как из самого класса, так и из других классов и из основного кода программы.
Кроме того, атрибуты допускается создавать динамически после создания класса — можно создать как атрибут объекта класса, так и атрибут экземпляра класса. Рассмотрим это на примере (листинг 13.4).
Листинг 13.4. Атрибуты объекта класса и экземпляра класса class MyClass: # Определяем пустой класс pass
MyClass.x = 50 # Создаем атрибут объекта класса c1, c2 = MyClass(), MyClass() # Создаем два экземпляра класса c1.y = 10 # Создаем атрибут экземпляра класса c2.y = 20 # Создаем атрибут экземпляра класса print(c1.x, c1.y) # Выведет: 50 10 print(c2.x, c2.y) # Выведет: 50 20
В этом примере мы определяем пустой класс, разместив в нем оператор pass
. Далее создаем атрибут объекта класса: x
. Этот атрибут будет доступен всем создаваемым экземплярам класса. Затем создаем два экземпляра класса и добавляем одноименные атрибуты: y
. Значе- ния этих атрибутов будут разными в каждом экземпляре класса. Но если создать новый эк- земпляр (например, c3
), то атрибут y
в нем определен не будет. Таким образом, с помощью

Глава 13. Объектно-ориентированное программирование
247 классов можно имитировать типы данных, поддерживаемые другими языками программи- рования (например, тип struct
, доступный в языке C).
Очень важно понимать разницу между атрибутами объекта класса и атрибутами экземпляра класса. Атрибут объекта класса доступен всем экземплярам класса, и после изменения атрибута значение изменится во всех экземплярах класса. Атрибут экземпляра класса мо- жет хранить уникальное значение для каждого экземпляра, и изменение его в одном экзем- пляре класса не затронет значения одноименного атрибута в других экземплярах того же класса. Рассмотрим это на примере, создав класс с атрибутом объекта класса (
x
) и атрибу- том экземпляра класса (
y
):
>>> class MyClass: x = 10 # Атрибут объекта класса def __init__(self): self.y = 20 # Атрибут экземпляра класса
Теперь создадим два экземпляра этого класса:
>>> c1 = MyClass() # Создаем экземпляр класса
>>> c2 = MyClass() # Создаем экземпляр класса
Выведем значения атрибута x
, а затем изменим значение и опять произведем вывод:
>>> print(c1.x, c2.x) # 10 10
>>> MyClass.x = 88 # Изменяем атрибут объекта класса
>>> print(c1.x, c2.x) # 88 88
Как видно из примера, изменение атрибута объекта класса затронуло значение в двух экземплярах класса сразу. Теперь произведем аналогичную операцию с атрибутом y
:
>>> print(c1.y, c2.y) # 20 20
>>> c1.y = 88 # Изменяем атрибут экземпляра класса
>>> print(c1.y, c2.y) # 88 20
В этом случае изменилось значение атрибута только в экземпляре c1
Следует также учитывать, что в одном классе могут одновременно существовать атрибут объекта и атрибут экземпляра с одним именем. Изменение атрибута объекта класса мы про- изводили следующим образом:
>>> MyClass.x = 88 # Изменяем атрибут объекта класса
Если после этой инструкции вставить инструкцию
>>> c1.x = 200 # Создаем атрибут экземпляра то будет создан атрибут экземпляра класса, а не изменено значение атрибута объекта клас- са. Чтобы увидеть разницу, выведем значения атрибутов:
>>> print(c1.x, MyClass.x) # 200 88 13.2. Методы __init__() и __del__()
При создании экземпляра класса интерпретатор автоматически вызывает метод инициали- зации
__init__()
. В других языках программирования такой метод принято называть кон- структором класса. Формат метода: def __init__(self[, <Значение1>[, ..., <ЗначениеN>]]):
<Инструкции>

248
Часть I. Основы языка Python
С помощью метода
__init__()
атрибутам класса можно присвоить начальные значения.
При создании экземпляра класса параметры этого метода указываются после имени класса в круглых скобках:
<Экземпляр класса> = <Имя класса>([<Значение1>[, ..., <ЗначениеN>]])
Пример использования метода
__init__()
приведен в листинге 13.5.
Листинг 13.5. Метод __init__() class MyClass: def __init__(self, value1, value2): # Конструктор self.x = value1 self.y = value2 c = MyClass(100, 300) # Создаем экземпляр класса print(c.x, c.y) # Выведет: 100 300
Если конструктор вызывается при создании экземпляра, то перед уничтожением экземпляра автоматически вызывается метод, называемый деструктором. В языке Python деструктор реализуется в виде предопределенного метода
__del__()
(листинг 13.6). Следует заметить, что метод не будет вызван, если на экземпляр класса существует хотя бы одна ссылка.
Впрочем, поскольку интерпретатор самостоятельно заботится об удалении объектов, ис- пользование деструктора в языке Python не имеет особого смысла.
Листинг 13.6. Метод __del__() class MyClass: def __init__(self): # Конструктор класса print("Вызван метод __init__()") def __del__(self): # Деструктор класса print("Вызван метод __del__()") c1 = MyClass() # Выведет: Вызван метод __init__() del c1 # Выведет: Вызван метод __del__() c2 = MyClass() # Выведет: Вызван метод __init__() c3 = c2 # Создаем ссылку на экземпляр класса del c2 # Ничего не выведет, т. к. существует ссылка del c3 # Выведет: Вызван метод __del__()
13.3. Наследование
Наследование является, пожалуй, самым главным понятием ООП. Предположим, у нас есть класс (например,
Сlass1
). При помощи наследования мы можем создать новый класс (на- пример,
Сlass2
), в котором будет реализован доступ ко всем атрибутам и методам класса
Сlass1
(листинг 13.7).
Листинг 13.7. Наследование class Class1: # Базовый класс def func1(self): print("Метод func1() класса Class1")

Глава 13. Объектно-ориентированное программирование
249 def func2(self): print("Метод func2() класса Class1") class Class2(Class1): # Класс Class2 наследует класс Class1 def func3(self): print("Метод func3() класса Class2") c = Class2() # Создаем экземпляр класса Class2 c.func1() # Выведет: Метод func1() класса Class1 c.func2() # Выведет: Метод func2() класса Class1 c.func3() # Выведет: Метод func3() класса Class2
Как видно из примера, класс
Class1
указывается внутри круглых скобок в определении класса
Class2
. Таким образом, класс
Class2
наследует все атрибуты и методы класса
Class1
Класс
Class1
называется базовым или суперклассом, а класс
Class2
— производным или подклассом.
Если имя метода в классе
Class2
совпадает с именем метода класса
Class1
, то будет исполь- зоваться метод из класса
Class2
. Чтобы вызвать одноименный метод из базового класса, перед методом следует через точку написать название базового класса, а в первом парамет- ре метода — явно указать ссылку на экземпляр класса. Рассмотрим это на примере
(листинг 13.8).
Листинг 13.8. Переопределение методов class Class1: # Базовый класс def __init__(self): print("Конструктор базового класса") def func1(self): print("Метод func1() класса Class1") class Class2(Class1): # Класс Class2 наследует класс Class1 def __init__(self): print("Конструктор производного класса")
Class1.__init__(self) # Вызываем конструктор базового класса def func1(self): print("Метод func1() класса Class2")
Class1.func1(self) # Вызываем метод базового класса c = Class2() # Создаем экземпляр класса Class2 c.func1() # Вызываем метод func1()
Выведет:
Конструктор производного класса
Конструктор базового класса
Метод func1() класса Class2
Метод func1() класса Class1
В
НИМАНИЕ
!
Конструктор базового класса автоматически не вызывается, если он переопределен в про- изводном классе.

250
Часть I. Основы языка Python
Чтобы вызвать одноименный метод из базового класса, также можно воспользоваться функцией super()
. Формат функции: super([<Класс>, <Указатель self>])
С помощью функции super()
инструкцию
Class1.__init__(self) # Вызываем конструктор базового класса можно записать так: super().__init__() # Вызываем конструктор базового класса или так: super(Class2, self).__init__() # Вызываем конструктор базового класса
Обратите внимание на то, что при использовании функции super()
не нужно явно переда- вать указатель self в вызываемый метод. Кроме того, в первом параметре функции super()
указывается производный класс, а не базовый. Поиск идентификатора будет производиться во всех базовых классах. Результатом поиска станет первый найденный идентификатор в цепочке наследования.
П
РИМЕЧАНИЕ
В последних версиях Python 2 существовало два типа классов: «классические» классы и классы нового стиля. Классы нового стиля должны были явно наследовать класс object.
В Python 3 все классы являются классами нового стиля, но наследуют класс object неявно.
Таким образом, все классы верхнего уровня являются наследниками этого класса, даже если он не указан в списке наследования. «Классические» классы (в понимании Python 2) в Python 3 больше не поддерживаются.
13.4. Множественное наследование
В определении класса в круглых скобках можно указать сразу несколько базовых классов через запятую. Рассмотрим множественное наследование на примере (листинг 13.9).
Листинг 13.9. Множественное наследование class Class1: # Базовый класс для класса Class2 def func1(self): print("Метод func1() класса Class1") class Class2(Class1): # Класс Class2 наследует класс Class1 def func2(self): print("Метод func2() класса Class2") class Class3(Class1): # Класс Class3 наследует класс Class1 def func1(self): print("Метод func1() класса Class3") def func2(self): print("Метод func2() класса Class3") def func3(self): print("Метод func3() класса Class3") def func4(self): print("Метод func4() класса Class3")

Глава 13. Объектно-ориентированное программирование
251 class Class4(Class2, Class3): # Множественное наследование def func4(self): print("Метод func4() класса Class4") c = Class4() # Создаем экземпляр класса Class4 c.func1() # Выведет: Метод func1() класса Class3 c.func2() # Выведет: Метод func2() класса Class2 c.func3() # Выведет: Метод func3() класса Class3 c.func4() # Выведет: Метод func4() класса Class4
Метод func1()
определен в двух классах:
Class1
и
Class3
. Так как вначале просматривают- ся все базовые классы, непосредственно указанные в определении текущего класса, метод func1()
будет найден в классе
Class3
(поскольку он указан в числе базовых классов в опре- делении
Class4
), а не в классе
Class1
Метод func2()
также определен в двух классах:
Class2
и
Class3
. Так как класс
Class2
стоит первым в списке базовых классов, то метод будет найден именно в нем. Чтобы наследовать метод из класса
Class3
, следует указать это явным образом. Переделаем определение класса
Class4
из предыдущего примера и наследуем метод func2()
из класса
Class3
(лис- тинг 13.10).
Листинг 13.10. Указание класса при наследовании метода class Class4(Class2, Class3): # Множественное наследование
# Наследуем func2() из класса Class3, а не из класса Class2 func2 = Class3.func2 def func4(self): print("Метод func4() класса Class4")
Вернемся к листингу 13.9. Метод func3()
определен только в классе
Class3
, поэтому метод наследуется от этого класса. Метод func4()
, определенный в классе
Class3
, переопределя- ется в производном классе.
Если искомый метод найден в производном классе, то вся иерархия наследования просмат- риваться не будет.
Для получения перечня базовых классов можно воспользоваться атрибутом
__bases__
В качестве значения атрибут возвращает кортеж. В качестве примера выведем базовые классы для всех классов из предыдущего примера:
>>> print(Class1.__bases__)
>>> print(Class2.__bases__)
>>> print(Class3.__bases__)
>>> print(Class4.__bases__)
Выведет:
(,)
(,)
(,)
(, )
Рассмотрим порядок поиска идентификаторов при сложной иерархии множественного на- следования (листинг 13.11).

252
Часть I. Основы языка Python
Листинг 13.11. Поиск идентификаторов при множественном наследовании class Class1: x = 10 class Class2(Class1): pass class Class3(Class2): pass class Class4(Class3): pass class Class5(Class2): pass class Class6(Class5): pass class Class7(Class4, Class6): pass c = Class7() print(c.x)
Последовательность поиска атрибута x
будет такой:
Class7 -> Class4 -> Class3 — > Class6 -> Class5 -> Class2 -> Class1
Получить всю цепочку наследования позволяет атрибут
__mro__
:
>>> print(Class7.__mro__)
Результат выполнения:
(, ,
, ,
, ,
, )
13.4.1. Примеси и их использование
Множественное наследование, поддерживаемое Python, позволяет реализовать интересный способ расширения функциональности классов с помощью так называемых примесей
(mixins). Примесь — это класс, включающий какие-либо атрибуты и методы, которые необ- ходимо добавить к другим классам. Объявляются они точно так же, как и обычные классы.
В качестве примера объявим класс-примесь
Mixin
, после чего объявим еще два класса, до- бавим к их функциональности ту, что определена в примеси
Mixin
, и проверим ее в дейст- вии (листинг 13.12).
Листинг 13.12. Расширение функциональности классов посредством примеси class Mixin: # Определяем сам класс-примесь attr = 0 # Определяем атрибут примеси def mixin_method(self): # Определяем метод примеси print("Метод примеси") class Class1 (Mixin): def method1(self): print("Метод класса Class1") class Class2 (Class1, Mixin): def method2(self): print("Метод класса Class2")

Глава 13. Объектно-ориентированное программирование
253 c1 = Class1() c1.method1() c1.mixin_method() # Class1 поддерживает метод примеси c2 = Class2() c2.method1() c2.method2() c2.mixin_method() # Class2 также поддерживает метод примеси
Вот результат выполнения кода, приведенного в листинге 13.12:
Метод класса Class1
Метод примеси
Метод класса Class1
Метод класса Class2
Метод примеси
Примеси активно применяются в различных дополнительных библиотеках — в частности, в популярном веб-фреймворке Django.
13.5. Специальные методы
Классы поддерживают следующие специальные методы:

__call__()
— позволяет обработать вызов экземпляра класса как вызов функции. Фор- мат метода:
__call__(self[, <Параметр1>[, ..., <ПараметрN>]])
Пример: class MyClass: def __init__(self, m): self.msg = m def __call__(self): print(self.msg) c1 = MyClass("Значение1") # Создание экземпляра класса c2 = MyClass("Значение2") # Создание экземпляра класса c1() # Выведет: Значение1 c2() # Выведет: Значение2

__getattr__(self, <Атрибут>)
— вызывается при обращении к несуществующему атри- буту класса: class MyClass: def __init__(self): self.i = 20 def __getattr__(self, attr): print("Вызван метод __getattr__()") return 0 c = MyClass()
# Атрибут i существует print(c.i) # Выведет: 20. Метод __getattr__() не вызывается
# Атрибут s не существует print(c.s) # Выведет: Вызван метод __getattr__() 0
1   ...   20   21   22   23   24   25   26   27   ...   83


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