5. ОСНОВЫ ОБЪЕКТНО-ОРИЕНТИРОВАННОГО ПРОГРАММИРОВАНИЯ В ЯЗЫКЕ PYTHON
Главная идея ООП состоит в том, чтобы объединить в одно целое данные
ифункции, предназначенные для обработки этих данных. Реализуется эта идея в классе. По большому счету класс – это некая конструкция, которая связывает или объединяет определенное количество переменных и определенное количество функций. На основе класса создаются объекты. Вообще, про класс удобно думать, как про некоторый шаблон, на основе которого затем "по образу и подобию" создаются объекты. Но важно понимать, что здесь речь идет не о создании клонов. То есть класс и объект, созданный на основе этого класса
– совершенно разные вещи (или сущности). Допустим, нам нужно собрать автомобиль. Завод у нас есть, и мы можем произвести любую деталь. Но этого мало. Нам нужен некий план или чертеж, который бы давал нам четкое и однозначное представление, какие детали в автомобиле, как и куда крепятся.
Понятно, что на самом деле там очень много чертежей для разных блоков
имеханизмов. Но в данном случае это не важно. Мы можем думать, что чертеж один. Вот этот чертеж, на основе которого собирается автомобиль – это аналог класса. А автомобиль, который собирается в соответствии с чертежом – аналог объекта, созданного на основе класса.
Все автомобили, собранные по этому чертежу, будут однотипными в смысле набора опций и функциональных возможностей. Если мы возьмем другой чертеж и соберем автомобиль по этому чертежу, то, очевидно, что получим автомобиль с несколько иными характеристиками (все зависит от чертежа – что в нем заложено, то и получим).
То есть ситуация следующая:
Есть чертеж автомобиля. Это аналог класса.
На основе чертежа можно собирать автомобили. Автомобиль, собранный на основе чертежа – аналог объекта, который создан на основе класса.
Вязыке Python – объектом является не только то, что мы создаем на основе класса, но и сам класс является объектом. Точно так же, как бумажный лист с чертежом является объектом, отличным от автомобиля, объект, которым является класс, отличается от объекта, который создается на основе класса. И здесь мы подходим к очень важному месту: к терминологии. Тот объект, который создается на основе класса, мы будем называть экземпляром касса. Тот объект, через который реализуется класс, будем называть объектом класса.
Создать класс можно следующим образом:
сlass имя_класса:
# блок тела класса
Все начинается с ключевого слова class, потом имя класса и двоеточие. Затем тело класса. При описании блок тела (инструкций) необходимо делать
40
отступы – четыре пробела. В теле класса, как правило, описывают методы. Метод – это та же функция, только вызываться она будет из экземпляра класса.
Формально метод в теле класса описывается как обычная функция. Но методы, как мы уже знаем, должны вызываться из экземпляра класса. При описании метода экземпляр класса, из которого будет вызываться метод, должен быть явно указан как первый аргумент метода. При вызове метода этот аргумент методу явно не передается. Причина в том, что экземпляр класса, из которого вызывается метод, указывается явно (перед именем метод через точку). Получается такое своеобразное правило, которое условно можно назвать "минус один аргумент": при вызове метода из экземпляра класса у него на один аргумент меньше, чем это было при описании метода (это если нет аргументов со значением по умолчанию)
Существует соглашение называть при описании метода первый его аргумент self. Мы тоже будем придерживаться этого соглашения – то есть в тех местах программного кода, где у метода объявлен аргумент с названием – self, этот аргумент будет означать ссылку на экземпляр класса, из которого вызывается метод.
После того, как класс создан, возникает следующий вопрос: как на основе класса создать экземпляр класса? Делается это очень просто с помощью команды вида переменная=класс (). Другими словами, после имени класса указываем круглые скобки (пока что пустые) и всю эту конструкцию присваиваем в качестве значения некоторой переменной. Как результат будет создан экземпляр класса, а ссылка на этот экземпляр записана в переменную.
Метод, описанный в классе как метод экземпляр класса, вызывается из экземпляра класса с помощью "точечного синтаксиса": после экземпляра класса через точку указывается имя вызываемого метода. При этом методу в круглых скобках передаются нужные аргументы. Если аргументы методу передавать не нужно, пустые круглые скобки после имени метода все равно указываем.
#создаем класс с названием MyClass Class MyClass :
#метод экземпляра класса
#единственный аргумент метод self – ссылка
# на экземпляр класса, из которого вызывается метод
def say_hello(self) :
#Методом отображается сообщение. Аргумент
#метода (ссылка self) явно не используется
print(‘Вас приветствует экземпляр класса !’)
#Создаем экземпляр класса obj = MyClass()
#Вызываем метод экземпляра класса
#При вызове аргументы методу не передаются obj.say_hello()
41
Класс и экземпляр класса
В результате выполнения данного программного кода в окне вывода появляются такое сообщение:
Вас приветствует экземпляр класса!
Мы создали класс с названием MyClass. В теле класса описан всего один метод, который называется say_hello(), и у этого метода, как несложно заметить, один аргумент, который называется self. В теле метода имеется одна команда print("Вас приветствует экземпляр класса!"), в результате выполнения которой, как мы не без оснований ожидаем, в окне вывода появится текстовое сообщение. Экземпляр класса создается командой obj=MyClass(). В данном случае экземпляр класса создается непосредственно при выполнении инструкции MyClass() , а ссылка на этот экземпляр записывается в переменную obj.
Хотя ничего неправильного или некорректного в этом нет, такой подход граничит с дурным тоном. Почему? Как минимум потому, что если нас устраивает ситуация, когда все экземпляры класса "ведут" себя одинаково, то возникают сомнения в необходимости использования ООП для решения соответствующей задачи. Проще говоря, здесь имеет место не совсем адекватное использование объектно-ориентированного подхода. Поэтому обычно методы, которые описываются как методы экземпляра класса, в том или ином виде используют ссылку на экземпляр класса, из которого вызывается метод (первый аргумент с рекомендованным названием self). Прежде, чем приступить к рассмотрению примера, в котором нам удастся избежать описанной выше досадной неприятности, расширим познания относительно содержимого экземпляров класса. Новость очень простая: у экземпляров класса могут быть не только методы, но и переменные, которые мы будем называть полями экземпляра касса. А все вместе, – поля и методы экземпляра класса, – будем называть атрибутами экземпляра касса. То есть атрибуты – это поля и методы.
Фактически поле экземпляра класса – это некоторая переменная, которая "приписана" (или "прикреплена") к этому экземпляру класса. Проблема здесь вот в чем: переменные, как мы знаем, появляются тогда, когда им присваивается значение. Если мы говорим о переменной в контексте экземпляра класса, то естественным образом возникает вопрос: где, в каком месте, переменной (полю) можно присвоить значение? Логично предположить, что при выполнении метода экземпляра класса.
#Создаем класс
Class MyClass:
#Метод для присваивания значения
#полю экземпляра класса
42
def set(self, n) :
рrint ( " Внимание ! Присваив ается значение! " )
# Полю присваивается значение self.nume r=n
#Метод для считывания значения
#поля э кземпляра класса
def get(self) :
# Отображаем значение поля
рrint ( " Значение пoля : " , s e l f . number )
#Создаем экземпляр класса obj =MyClass( )
#Вызывается метод экземпляра класса и
#полю экземпляра класса присваивается значение obj.set(100)
#Вызывается метод экземпляра класса и
#отображается значение поля экземпляра класса obj.get( )
Поле экземпляра класса
При выполнении этого программного кода в окне вывода появляется два сообщения, как показано ниже:
Внимание! Присваивается значение! Значение поля: 100
Мы, как и в предыдущем примере, создали класс с названием MyClass. Но теперь в этом классе описаны два метода экземпляра класса: метод set() предназначен для присваивания значения полю экземпляра класса. Поле называется number, и о его существовании мы узнаем исключительно из программного кода метода set() : в том месте, где командой self.number=n полю number экземпляра класса, на который ссылается переменная self, присваивается значение переменной n. И переменная self (первый аргумент), и переменная n (второй аргумент) объявлены как аргументы метода set(). Переменная self представляет собой ссылку на экземпляр класса, из которого вызывается метод, а переменная n – это непосредственно тот аргумент, который передается методу при вызове. В последствии, когда командой obj = MyClass() будет создан экземпляр obj класса MyClass и затем командой obj.set(100) из этого экземпляра вызван метод set() с аргументом 100, данное значение будет присвоено полю number экземпляра obj.
Значение полю экземпляра класса можно присвоить через прямое обращение к этому полю. Рассмотрим небольшой пример в листинге:
# Создаем класс без методов class MyClass:
pass
43
#Создаем экземпляр класса obj = MyClass()
#Присваивается значение полю number
#экземпляра obj
obj.number = 100
#От ображается значение поля number
#экземпляра obj
рrint ("Значение пoля: ", obj.number)
Значение поля экземпляра класса
Резульат выполнения программного кода такой:
Значение поля: 100
В данном случае мы создаем класс, в котором вообще ничего нет – никакие методы в теле MyClass класса не описываются. Там есть только инструкция pass. С помощью этой инструкции мы выделяем тело класса. Проблема в том, что если там не написать вообще ничего, то такой синтаксис будет содержать ошибку. Поэтому даже если класс ничего не содержит, что-то там все равно должно быть. В таких случаях используем формальную ничего не значащую инструкцию pass.
Экземпляр класса, как и во всех предыдущих случаях, создаем командой obj = MyClass(). Затем командой obj.number = 100 экземпляр obj получает поле number, и этому полю присваивается значение 1 0 0. После этого значение поля nuшber экземпляра obj считывается и отображается в окне вывода командой print ("Значение поля:" obj.number).
Ситуация, когда значения полям экземпляров класса присваиваются (явно или посредством специальных методов) уже после создания экземпляра класса не очень удобна. Особенно она неудобна, если приходится иметь дело со значительным количеством экземпляров. Существует механизм, который позволяет частично (или даже полностью) снять эту проблему. Речь идет об использовании конструктора
5.1. Конструкторы и декораторы
При создании класса можно описать специальный метод, который называется конструктором экземпляра класса (иногда также называют методом инициализации). Этот метод автоматически вызывается при создании экземпляра класса. Рецепт создания конструктора очень простой: необходимо создать метод с названием __init__() (два символа подчеркивания в начале и два символа подчеркивания в конце). Аргументов у конструктора может быть сколько угодно – но не меньше одного (ссылка на экземпляр, при создании которого вызывается конструктор)
44