Пример 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:
>>> from person import Person >>> from manager import Manager
>>> bob = Person(name='Bob Smith', age=42, pay=10000)
>>> sue = Person(name='Sue Jones', age=45, pay=20000)
>>> tom = Manager(name='Tom Doe', age=55, pay=30000)
>>> db = [bob, sue, tom]
>>> for obj in db:
obj.giveRaise(.10) # метод по умолчанию или специализированный >>> for obj in db:
print(obj.lastName(), ‘=>', obj.pay)
Smith => 11000.0 Jones => 22000.0 Doe => 36000.0
Реструктуризация программного кода
Прежде чем двинуться дальше, рассмотрим еще несколько альтернативных вариантов реализации. Большинство из них подчеркивают преимущества модели ООП в Python и рассматриваются здесь для краткого знакомства.
Расширение методов
Во-первых, обратите внимание на некоторую избыточность в примере 1.16: расчет увеличения оклада производится в двух местах (в двух классах). Мы могли бы реализовать специализированный класс Manager, не замещая унаследованный метод giveRaise новой реализацией, а расширяя его:
class Manager(Person):
def giveRaise(self, percent, bonus=0.1):
Person.giveRaise(self, percent + bonus)
Вся хитрость заключается в непосредственном вызове версии метода суперкласса и явной передаче ему аргумента self. При таком подходе мы также переопределяем метод, но на этот раз мы просто вызываем универсальную версию после добавления 10-процентной надбавки (предусмотренной по умолчанию) к указанному значению в процентах. Этот прием позволяет уменьшить избыточность программного кода (оригинальная логика метода giveRaise находится в одном только месте, что упрощает возможность ее изменения в будущем), и его особенно удобно использовать при переопределении методов-конструкторов суперклассов.
Если вы уже знакомы с особенностями ООП в Python, то должны знать, что этот прием работает благодаря возможности вызова методов как относительно экземпляра, так и относительно имени класса. Вообще говоря, следующие два вызова являются эквивалентными, и можно использовать обе формы:
instance.method(arg1, arg2) class.method(instance, arg1, arg2)
В действительности, первая форма отображается во вторую - при вызове метода относительно экземпляра интерпретатор Python отыскивает в дереве наследования ближайший класс, в котором имеется требуемый метод, и вызывает его, автоматически передавая экземпляр в первом аргументе. В любом случае, внутри метода giveRaise аргумент self будет ссылаться на экземпляр, являющийся объектом вызова.
Читать дальше
Конец ознакомительного отрывка
Купить книгу