• инстанцирует (создает) новый объект в памяти;
• вызывает метод объекта __init__, передавая только что созданный объект под именем self и другой аргумент ('Elmer Fudd') в качестве значения параметра name;
• сохраняет в объекте значение переменной name;
• возвращает новый объект;
• прикрепляет к объекту имя hunter.
Этот новый объект похож на любой другой объект Python. Вы можете использовать его как элемент списка, кортежа, словаря или множества. Можете передать его в функцию как аргумент или вернуть его в качестве результата.
Что насчет значения name, которое мы передали? Оно было сохранено как атрибут объекта. Вы можете прочитать и записать его непосредственно:
>>> print('The mighty hunter: ', hunter.name)
The mighty hunter: Elmer Fudd
Помните, внутри определения класса Person вы получаете доступ к атрибуту name с помощью конструкции self.name. Когда вы создаете реальный объект вроде hunter, то ссылаетесь на этот атрибут как hunter.name.
Не обязательно иметь метод __init__ в описании каждого класса, он используется для того, чтобы различать объекты одного класса.
Наследование
Может случиться, что, пытаясь решить какую-то задачу, вы обнаружите, что уже существует класс, создающий объекты, которые делают почти все из того, что вам нужно. Что вы можете предпринять? Вы можете модифицировать этот старый класс, но при этом сделаете его сложнее и можете сломать что-то, что раньше работало.
Конечно, можно написать новый класс, скопировав и вставив содержимое старого класса, а затем дополнить его новым кодом. Но это значит, что вам придется поддерживать больше кода и части старого и нового классов могут быть непохожими друг на друга, поскольку теперь находятся в разных местах.
Решением проблемы является наследование — создание нового класса из уже существующего, который при этом содержит какие-то дополнения и изменения. Это отличный способ использовать код повторно. Когда вы применяете наследование, новый класс может автоматически использовать весь код старого класса и при этом вам не нужно его копировать.
Вы определяете только то, что вам нужно добавить или изменить в новом классе, и этот код переопределяет поведение старого класса. Оригинальный класс называется предком, суперклассом или базовым классом, новый класс называется потомком, подклассом или классом-наследником. Эти термины в объектно-ориентированном программировании взаимозаменяемы.
Давайте же что-нибудь унаследуем. Мы определим пустой класс, который называется Car. Далее определим подкласс класса Car, который называется Yugo. Вы определяете подкласс с помощью все того же ключевого слова class, но указывая внутри скобок имя родительского класса (class Yugo(Car), как показано ниже):
>>> class Car():
…·····pass
…
>>> class Yugo(Car):
…·····pass
…
Далее создадим объекты каждого класса:
>>> give_me_a_car = Car()
>>> give_me_a_yugo = Yugo()
Класс-потомок является уточненной версией класса-предка; если говорить в терминах объектно-ориентированных языков, Yugo является Car. Объект с именем give_me_a_yugo является экземпляром класса Yugo, но он также наследует все то, что может делать класс Car. В нашем случае классы Car и Yugo полезны как мертвому припарки, поэтому попробуем указать их новые определения, которые действительно могут что-то сделать:
>>> class Car():
…·····def exclaim(self):
…·········print("I'm a Car!")
…
>>> class Yugo(Car):
…·····pass
…
Наконец, создадим по одному объекту каждого класса и вызовем их методы exclaim:
>>> give_me_a_car = Car()
>>> give_me_a_yugo = Yugo()
>>> give_me_a_car.exclaim()
I'm a Car!
>>> give_me_a_yugo.exclaim()
I'm a Car!
Не сделав ничего особенного, класс Yugo унаследовал метод exclaim() класса Car. Фактически класс Yugo говорит, что он является классом Car, что может привести к кризису самоопределения. Посмотрим, что мы можем с этим сделать.
Перегрузка метода
Как вы только что увидели, новый класс наследует все, что находится в его классе-предке. Далее вы увидите, как можно заменить, или перегрузить, родительские методы. Класс Yugo должен как-то отличаться от класса Car, иначе зачем вообще создавать новый класс. Изменим способ работы метода exclaim() для класса Yugo:
>>> class Car():
…·····def exclaim(self):
…·········print("I'm a Car!")
…
>>> class Yugo(Car):
…·····def exclaim(self):
…·········print("I'm a Yugo! Much like a Car, but more Yugo-ish.")
…
Теперь создадим объекты этих классов:
>>> give_me_a_car = Car()
>>> give_me_a_yugo = Yugo()
Что они говорят?
>>> give_me_a_car.exclaim()
I'm a Car!
>>> give_me_a_yugo.exclaim()
I'm a Yugo! Much like a Car, but more Yugo-ish.
В этих примерах мы перегрузили метод exclaim(). Перегрузить можно любые методы, включая __init__(). Рассмотрим другой пример, который использует наш более старый класс Person. Создадим подклассы, которые представляют докторов (MDPerson) и адвокатов (JDPerson):
>>> class Person():
…·····def __init__(self, name):
…·········self.name = name
…
>>> class MDPerson(Person):
…·····def __init__(self, name):
…·········self.name = "Doctor " + name
…
>>> class JDPerson(Person):
…·····def __init__(self, name):
…·········self.name = name +", Esquire"
…
В этих случаях метод инициализации __init__() принимает те же аргументы, что и родительский класс Person, но внутри объекта сохраняет значение переменной name разными способами:
>>> person = Person('Fudd')
>>> doctor = MDPerson('Fudd')
>>> lawyer = JDPerson('Fudd')
>>> print(person.name)
Fudd
>>> print(doctor.name)
Doctor Fudd
>>> print(lawyer.name)
Fudd, Esquire
Добавление метода
В класс-потомок можно также добавить метод, которого не было в родительском классе. Возвращаясь к классам Car и Yugo, мы определим новый метод need_a_push() только для класса Yugo:
>>> class Car():
…·····def exclaim(self):
…·········print("I'm a Car!")
…
>>> class Yugo(Car):
…·····def exclaim(self):
…·········print("I'm a Yugo! Much like a Car, but more Yugo-ish.")
…·····def need_a_push(self):
…·········print("A little help here?")
…
Далее создадим объекты классов Car и Yugo:
>>> give_me_a_car = Car()
>>> give_me_a_yugo = Yugo()
Объект класса Yugo может реагировать на вызов метода need_a_push():
>>> give_me_a_yugo.need_a_push()
A little help here?
А объект общего класса Car — нет:
>>> give_me_a_car.need_a_push()
Traceback (most recent call last):
··File "", line 1, in
AttributeError: 'Car' object has no attribute 'need_a_push'
К этому моменту класс Yugo может делать что-то, чего не может делать класс Car, и теперь мы точно можем определить отдельную личность класса Yugo.
Просим помощи у предка с помощью ключевого слова super
Мы видели, как класс-потомок может добавить или перегрузить метод класса-предка. Но что, если вам нужно вызвать оригинальный метод родительского класса? «Рад, что вы спросили», — говорит метод super(). Мы определим новый класс, который называется EmailPerson и представляет объект класса Person, содержащий адрес электронной почты. Для начала запишем наше привычное определение класса Person:
>>> class Person():
…·····def __init__(self, name):
…·········self.name = name
…
Обратите внимание на то, что вызов метода __init__() в следующем подклассе имеет дополнительный параметр email: