73. Основы написания классов в Python
Оглавление урока
Введение
В предыдущем уроке мы поверхностно пробежались по основным отличительным особенностям классов от ранее изучаемых средств. В этом уроке на основе простых примеров будем разбираться более детально с оператором class
и его преимуществами.
Объект класса или экземпляров? Под объектом класса понимается сама «фабрика» для производства экземпляров. Вспомним класс Wheel
из предыдущего урока:
class Wheel:
pressure = 2.0
def forward_rotation(self):
pass
def backwards_rotation(self):
pass
И его использование:
fl = Wheel()
Выполнение оператора class
создает объект класса и присваивает ему имя Wheel
, который мы в дальнейшем используем в роли «фабрики» для экземпляров.
Объект fl
является экземпляром. Таких экземпляров может быть бесчисленное множество.
Перегрузка операторов
В предыдущем уроке мы упоминали про перегрузку операторов, когда разбирались с конструктором класса. Создадим простой класс, в котором будет определен конструктор для инициализации атрибута data
.
class SimpleSum:
data = 0
def __init__(self, d):
self.data = d
num = SimpleSum(10)
print(num.data) # => 10
print(num + 10) # => ???
Мы создали экземпляр num
, и вывели значение атрибута data
. Но что произойдет, если мы попробуем прибавить к num
число 10
? Будет поднято исключение TypeError
(про исключения мы говорили в уроке «Обработка исключений (try/except) в Python»).
Оператор «+
» можно перегрузить точно также, как и конструктор класса __init__
. Метод __add__
будет выполнятся, если экземпляр класса SimpleSum
будет встречен в выражении +
.
class SimpleSum:
data = 0
def __init__(self, d):
self.data = d
def __add__(self, second):
self.data = self.data + second
num = SimpleSum(10)
print(num.data) # => 10
num + 10
print(num.data) # => 20
Таким образом, мы перегрузили оператор «+
» и теперь Python понимает, что не нужно пытаться прибавить к объекту класса SimpleSum
простое число, а следует прибавить к атрибуту data
, этого экземпляра и сохранить результат в нем же. К перегрузке операторов мы еще вернемся.
Другие атрибуты класса и снова про наследование
Модель наследования очень проста, она сводится к поиску атрибутов среди связанных объектов. Давайте создадим пустой класс:
class Empty:
pass
С оператором pass
вы знакомы из урока «Операторы break, continue и pass в Python». С большим успехом мы можем присоединить к нему новые атрибуты, например:
Empty.name = "Иван"
print(Empty.name)
Теперь посмотрим на модель наследования в действии. Создадим два экземпляра из объекта Empty
.
Empty.name = "Иван"
x = Empty()
y = Empty()
print(x.name) # => Иван
print(y.name) # => Иван
Фактически, экземпляры не имеют атрибутов – они берут их из объекта класса. В следующем примере экземпляр x
по-прежнему наследует атрибут name
, но мы присвоили ему другое значение:
x.name = "Петр"
print(x.name) # => Петр
print(y.name) # => Иван
Как мы будем говорить в дальнейшем, атрибуты объекта реализуются как словари, которые связаны с другими словарями:
Empty.__dict__.keys() # => dict_keys(['__module__', '__dict__', '__weakref__', '__doc__', 'name'])
x.__dict__.keys() # => dict_keys(['name'])
Возвращаясь к поиску в иерархии наследования, каждый экземпляр имеет ссылку на свой класс, которую можно получить следующим образом:
x.__class__ # => <class '__main__.Empty'>
Также можно получить кортеж ссылок из суперклассов для объекта, например:
print(Empty.__bases__) # => (<class 'object'>,)
Наш класс Empty
наследуется только от общего суперкласса object
, являющимся родителем для всех и о котором будем говорить в дальнейшем.
Ранее было показано, как динамически создать атрибут для класса. Теперь попробуем добавить метод в наш пустой класс Empty
. Напомню про отличие функции от метода класса, в последнем есть ссылка на сам объект (self
). Учтем это при написании функции:
def print_name(obj):
print(obj.name)
Empty.name = "Иван"
print_name(Empty) # => Иван
Теперь сделаем функцию print_name()
методом класса Empty
с именем method_print()
.
Empty.name = "Иван"
Empty.method_print = print_name
x = Empty()
x.method_print() # => Иван
В этом уроке мы разграничили понятия экземпляр и объект класса, немного углубились в перегрузку операторов и начали вникать в наследование классов. Конечно, о многом не было сказано и какие-то вещи остались в тени, но вы не беспокойтесь, в последующих уроках мы будем раскладывать все по полочкам и углубляться в каждую тему.