Здесь атрибут__class__содержит ссылку на ближайший класс, экземпляром которого является объект self, даже при том, что метод__str__
может оказаться унаследованной версией. Метод__str__позволяет вы
водить экземпляры непосредственно, вместо того чтобы выводить отдельные атрибуты. В метод__str__можно было бы добавить цикл, выполняющий обход словаря атрибутов__dict__экземпляра и отобража
ющий все атрибуты. Но это лишь краткий обзор, поэтому оставим это предложение для самостоятельного упражнения.
Мы могли бы даже реализовать метод__add__, чтобы оператор + авто
матически вызывал метод giveRaise. Нужно ли это - другой вопрос. Использование оператора + для увеличения оклада может быть истолковано неправильно теми, кто впоследствии будет читать наш программный код.
Специализация конструктора
Наконец, обратите внимание, что в примере 1.16 при создании экземпляра класса Manager мы не передаем конструктору аргумент job. При необходимости мы могли бы передавать это значение в виде именованного аргумента, как показано ниже:
tom = Manager(name=’Tom Doe’, age=50, pay=50000, job=’manager’)
Причина, по которой мы в примере не включили передачу аргумента job, заключается в том, что в этом нет необходимости: если создается новый экземпляр класса Manager, занимаемая должность уже подразумевается классом. Тем не менее, чтобы не оставлять поле job пустым, возможно, имеет смысл явно реализовать конструктор для класса Manager, который будет заполнять это поле автоматически:
class Manager(Person):
def __init__(self, name, age, pay):
Person.__init__(self, name, age, pay, ‘manager’)
Теперь при создании экземпляра класса Manager его поле job будет заполняться автоматически. Вся хитрость заключается в явном вызове версии метода суперкласса, так же, как мы делали при реализации метода giveRaise выше. Единственное отличие здесь - необычное имя метода-конструктора.
Альтернативные классы
В последующих примерах мы не будем использовать ни одно из трех расширений, представленных в этом разделе, но для демонстрации их в действии соберем все эти идеи в примере 1.17, где представлены альтернативные реализации классов Person и Manager.
Альтернативные реализации классов Person и Manager с данными, методами и с перегрузкой операторов (не используется в объектах, предусматривающих возможность сохранения)
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)
def __str__(self):
return (‘<%s => %s: %s, %s>’ %
(self.__class__.__name__, self.name, self.job, self.pay))
class Manager(Person):
класс со специализированным методом giveRaise, наследующий обобщенные методы lastName и __str__
def __init__(self, name, age, pay):
Person.__init__(self, name, age, pay, ‘manager’)
def giveRaise(self, percent, bonus=0.1):
Person.giveRaise(self, percent + bonus)
if__name__== ‘__main__’:
bob = Person(‘Bob Smith’, 44)
sue = Person(‘Sue Jones’, 47, 40000, ‘hardware’)
tom = Manager(name=’Tom Doe’, age=50, pay=50000)
print(sue, sue.pay, sue.lastName())
for obj in (bob, sue, tom):
obj.giveRaise(.10) # вызовет метод giveRaise объекта obj print(obj) # вызовет обобщенную версию метода __str__
Обратите внимание на полиморфизм в цикле for, находящемся в программном коде самопроверки этого модуля: все три объекта используют один и тот же конструктор, метод lastName и методы вывода, но при обращении к методу giveRaise вызывается версия в зависимости от класса, на основе которого был создан экземпляр. Если запустить сце-
нарий из примера 1.17, он выведет в стандартный поток вывода приведенные ниже строки; поле job в экземпляре класса Manager заполняется конструктором, форматированный вывод наших объектов осуществляется с помощью нового метода__str__, а новая версия метода giveRaise
в классе Manager действует точно так же, как и прежде: