На предыдущем занятии мы с вами разобрались с понятием класса в Python, его атрибутами
и посмотрели как создаются экземпляры класса. Теперь ответим на вопрос: что же
может содержать класс? По сути, только две вещи: атрибуты (свойства) и методы. Атрибуты
– это переменные класса, а методы – это функции, реализующие определенную
логику (то есть, функциональность) класса. Далее, мы будем использовать термин метод,
подразумевая под ним функцию класса. Имена методов, обычно, являются глаголами,
т.к. выполняют определенные действия. В то время, как имена атрибутов – это
существительные, т.к. лишь хранят определенные данные.
Давайте, для
примера объявим метод setCoords в классе Point:
class Point:
x = 1; y = 1
def setCoords(self):
pass
Здесь сразу
бросается в глаза вот этот параметр self. Зачем он
здесь, если мы пока ничего не собираемся передавать этому методу? Так требуется
делать по синтаксису самого Питона: когда мы объявляем какой-либо метод внутри
класса, то обязательно должны указывать такой первый параметр. Теперь нужно
разобраться: что он означает? При вызове этого метода, например, через
экземпляр pt:
pt = Point()
pt.setCoords()
параметр self будет ссылаться
на этот объект-экземпляр, причем, Python сам
автоматически подставит нужное значение. Поэтому в скобках при вызове метода
ничего писать не нужно.
Давайте в этом
убедимся и выведем список локальных атрибутов экземпляра, на который ссылается self:
def setCoords(self):
print( self.__dict__ )
И при запуске
видим пустой список. Пропишем координаты в экземпляре pt:
Теперь, при
запуске видим две записи для x и y. То есть, self действительно
ссылается на тот же объект, что и pt.
Раз это так, то
мы с помощью метода setCoords можем переопределять координаты нашей
точки:
def setCoords(self, x, y):
self.x = x
self.y = y
И, далее,
вызвать этот метод:
pt.setCoords(5, 10)
print( pt.__dict__ )
Смотрите, мы
здесь автоматически (через self) добавляем локальные атрибуты для
экземпляра pt. То есть,
указанные координаты будут принадлежать только объекту pt и никак не
повлияют на значения других экземпляров. Что, в общем то, мы и ожидаем от
использования объекта pt.
И здесь, опять
же обратите внимание, что метод setCoords – общий для всех возможных
экземпляров класса Point. Когда мы с вами на предыдущем занятии
рассматривали переменные внутри класса, то об этом подробно говорили. Поведение
методов абсолютно такое же: объявленные внутри класса они становятся общими для
всех экземпляров, т.е. это будет одна и та же функция, а не копии внутри
экземпляров. И только благодаря первому параметру self мы можем
«знать» из какого конкретно экземпляра был вызван данный метод: self всегда ссылается
на этот экземпляр. А вот, если вызвать этот метод непосредственно из класса:
то возникнет
ошибка, т.к. в этом случае нужно явно указать первый аргумент:
Point.setCoords(pt, 5, 10)
Конструктор класса
Теперь
предположим, что мы хотели бы в момент создания экземпляра класса сразу
указывать нужные координаты и прописывать их как локальные атрибуты. Это можно
сделать с помощью специального метода:
__init__()
который
вызывается при создании объекта:
def __init__(self):
print("создание экземпляра класса Point")
И, при
выполнении:
будет вызван
данный метод. В ООП он имеет специальное название – конструктор. Итак,
укажем в этом конструкторе еще два аргумента и создадим в нем локальные
атрибуты:
def __init__(self, x, y):
self.x = x
self.y = y
Теперь создать
объект можно, только явно указав два аргумента:
pt = Point(5, 10)
print( pt.__dict__ )
и, как видите, у
нас получается полноценный экземпляр класса с двумя локальными координатами,
связанными только с этим объектом.
Для большей
гибкости, укажем в конструкторе аргументы x, y по умолчанию:
def __init__(self, x = 0, y = 0):
и тогда можно
вызывать конструктор следующими способами:
pt = Point()
pt2 = Point(5)
pt3 = Point(5, 10)
print( pt.__dict__, pt2.__dict__, pt3.__dict__, sep="\n" )
Деструктор класса
Противоположный конструктору метод:
__del__()
автоматически
вызывается при уничтожении экземпляра класса. Он называется деструктор.
Давайте запишем
вот такой деструктор в наш класс Point:
def __del__(self):
print("Удаление экземпляра: "+self.__str__())
и, запуская
программу, мы видим, что все наши три объекта были уничтожены при завершении
программы.
Но, в какой
момент вообще происходит удаление объекта в Python? Алгоритм
прост: пока на какой-либо объект имеется хотя бы одна внешняя ссылка, он
продолжает существовать. Если же на объект нет внешних ссылок, он автоматически
уничтожается «сборщиком мусора». Например, если создать экземпляр:
то на него
будет ссылаться переменная pt. Но, при ее переопределении, например,
так:
экземпляр класса
Point в памяти
остается без внешних ссылок и вскоре будет удален сборщиком мусора. При
удалении, как раз и будет вызван деструктор класса __del__().
Задания для самоподготовки
1. Создайте
класс Point3D, который хранит
координаты в виде списка. Пропишите у него конструктор для создания экземпляров
с локальными координатами. Также добавьте методы, позволяющие изменять
координаты и получать их (в виде кортежа).
2. Объявите
класс Point с
конструктором, который бы позволял создавать экземпляр на основе другого, уже
существующего. Если аргументы в конструктор не передаются, то создается объект
с локальными атрибутами по умолчанию.
3. Напишите
программу, в которой пользователь вводит координаты x, y с клавиатуры,
создается соответствующий экземпляр и он сохраняется в списке. Количество
вводимых объектов N=5. Затем, вывести их атрибуты в консоль.