Добавляем поведение
Пока что наш класс - это всего лишь данные: он заменил ключи словаря атрибутами объекта, но не добавляет ничего нового сверх того, что у нас было прежде. Чтобы задействовать всю мощь классов, необходимо добавить реализацию поведения. Заключая реализацию поведения в методы класса, мы сможем изолировать клиентов от влияния изменений в будущем. А объединяя методы в единое целое с данными, мы обеспечиваем естественное место, где другие будут искать наш программный код. В некотором смысле классы объединяют в себе записи и программы, обрабатывающие эти записи, - методы реализуют логику интерпретации и изменения данных (этот стиль программирования потому и называется объектно-ориентированным, что при таком подходе всегда обрабатываются данные объектов).
Например, в примере 1.15 добавляется логика получения фамилии и увеличения оклада в виде методов. Для доступа к обрабатываемому экземпляру (записи) методы используют аргумент self.
Пример 1.15. PP4E\Preview\person.py
class Person:
def __init__(self, name, age, pay=0, job=None):
self.name = name self.age = age self.pay = pay self.job = job def lastName(self):
return self.name.split()[-1] def giveRaise(self, percent): self.pay *= (1.0 + percent)
if__name__== ‘__main__’:
bob = Person(‘Bob Smith’, 42, 30000, ‘software’) sue = Person(‘Sue Jones’, 45, 40000, ‘hardware’) print(bob.name, sue.pay)
print(bob.lastName())
sue.giveRaise(.10)
print(sue.pay)
Если запустить этот сценарий, он выведет те же результаты, что и предыдущий, но теперь эти результаты возвращаются методами, а не извлекаются с помощью жестко зашитой логики, которая всегда оказывается избыточной, когда бы она ни применялась:
Bob Smith 40000
Smith
44000.0
Добавляем наследование
И наконец, рассмотрим еще одно расширение наших записей, прежде чем перейти к реализации их сохранения: поскольку теперь записи реализованы в виде класса, они обретают естественную возможность специализации через механизм наследования в Python. Пример 1.16 специализирует предыдущий класс Person, предусматривая 10-процентную надбавку, добавляемую при увеличении оклада менеджеров (любые совпадения с реальными примерами из жизни являются случайными).
Пример 1.16. PP4E\Preview\manager.py
from person import Person
class Manager(Person):
def giveRaise(self, percent, bonus=0.1): self.pay *= (1.0 + percent + bonus)
if__name__== ‘__main__’:
tom = Manager(name=’Tom Doe’, age=50, pay=50000)
print(tom.lastName())
tom.giveRaise(.20)
print(tom.pay)
Если запустить этот сценарий, он выведет следующее:
Doe
65000.0
Здесь объявление класса Manager находится в отдельном модуле, но это объявление точно так же можно поместить в модуль person (Python не требует создавать отдельные модули для каждого класса). Он наследует конструктор и метод lastName от своего суперкласса и специализирует метод giveRaise (как будет показано позднее, существуют различные способы реализации этого расширения). Поскольку данное дополнение было оформлено в виде нового подкласса, оно никак не отразится на работе экземпляров оригинального класса Person. Экземпляры, представляющие информацию о Бобе и Сью, например, унаследуют оригинальную логику увеличения оклада, а экземпляр, представляющий информацию о Томе, получит специализированную версию, потому что он является экземпляром другого класса. В ООП программы разрабатываются за счет специализации программного кода, а не его изменения.
Фактически программа, использующая наши объекты, вообще ничего не должна знать об особенностях реализации метода giveRaise - объект сам определяет порядок выполнения той или иной операции, опираясь на класс, экземпляром которого он является. Пока объект поддерживает ожидаемый интерфейс (в данном случае метод giveRaise), он будет совместим с вызывающей программой, независимо от конкретного типа объекта и даже независимо от особенностей реализации этого метода, которая может действовать совершенно иначе.
Если вам уже приходилось изучать язык Python, возможно, вы знаете, что такое поведение называется полиморфизмом. Это одно из основных свойств языка, и оно объясняет значительную долю гибкости программного кода. Результат вызова метода giveRaise в следующем фрагменте зависит от того, к какому классу принадлежит обрабатываемый объект obj, - Том получит 20-процентное повышение оклада, а не 10-процентное, потому что соответствующий ему экземпляр является экземпляром специализированного класса Manager: