Наследование в Python 3
Python | Комментировать запись
Объектно-ориентированное программирование позволяет создавать многоразовые шаблоны кода и таким образом уменьшать количество повторяемых блоков кода в программе. Один из методов, с помощью которого это достигается, называется наследованием; при этом один подкласс может использовать код из другого базового класса.
Данное руководство ознакомит вас с основными аспектами наследования в Python: с работой родительских и дочерних классов, переопределением методов и атрибутов, функцией super() и множественным наследованием.
Что такое наследование?
Наследование подразумевает, что дочерний класс использует код, созданный в родительском классе.
Дочерний класс, или подкласс – это класс, который наследует код из родительского, или базового класса.
Предположим, что у нас есть класс Parent с переменными last_name, height, и eye_color. Подкласс Child может наследовать эти переменные из класса Parent, то есть повторно использовать этот код. Это позволяет уменьшить объём кода и снизить избыточность.
Родительские классы
Родительский, или базовый класс создаёт шаблон кода, который в дальнейшем может наследоваться дочерними классами. Родительским классом может быть любой класс. Базовые классы – это не просто шаблоны, а полноценные функционирующие классы.
Предположим, у нас есть общий родительский класс Bank_account с дочерними классами Personal_account и Business_account. Многие параметры классов Personal_account и Business_account будут совпадать. Такие параметры можно наследовать из родительского класса Bank_account. В подклассе Business_account будут индивидуальные параметры (например, методы сбора деловых документов и форм и переменная employee_identification_number).
Аналогичным образом, класс Animal может содержать методы eating() и sleeping(), а подкласс Snake помимо вышеперечисленных наследуемых методов может также содержать методы hissing() и slithering().
Для примера попробуйте создать родительский класс Fish (в дальнейшем мы используем его, чтобы создать различные подклассы с типами рыб).
Создайте файл fish.py и добавьте в него метод конструктора __init__(), который будет содержать переменные first_name и last_name для каждого подкласса (или объекта) Fish.
Читайте также: Создание классов и определение объектов в Python 3
class Fish:
def __init__(self, first_name, last_name="Fish"):
self.first_name = first_name
self.last_name = last_name
Переменная last_name содержит строку “Fish”, потому что почти все подклассы будут использовать это значение.
Добавьте в файл другие методы:
class Fish:
def __init__(self, first_name, last_name="Fish"):
self.first_name = first_name
self.last_name = last_name
def swim(self):
print("The fish is swimming.")
def swim_backwards(self):
print("The fish can swim backwards.")
Теперь в файле есть методы swim() и swim_backwards(), которые будут наследоваться подклассами.
Добавьте другие атрибуты в метод __init__():
class Fish:
def __init__(self, first_name, last_name="Fish",
skeleton="bone", eyelids=False):
self.first_name = first_name
self.last_name = last_name
self.skeleton = skeleton
self.eyelids = eyelids
def swim(self):
print("The fish is swimming.")
def swim_backwards(self):
print("The fish can swim backwards.")
Создание родительского класса ничем не отличается от создания обычного класса. Единственный нюанс: нужно заранее продумывать, какие параметры смогут наследовать дочерние классы.
Дочерние классы
Дочерние классы, или подклассы – это классы, которые наследуют параметры родительских классов. Каждый дочерний класс сможет использовать методы и переменные родительского класса.
К примеру, дочерний класс Goldfish может наследовать из класса Fish функцию swim().
Дочерние классы начинаются немного иначе. В первой строке нужно передать родительский класс:
class Trout(Fish):
Класс Trout является дочерним по отношению к классу Fish (родительский класс нужно указать в круглых скобках).
В дочернем классе можно добавить больше методов, переопределить методы родительского класса или просто принять его методы с помощью ключевого слова pass, например:
...
class Trout(Fish):
pass
Теперь создайте объект Trout и наследуйте методы родительского класса, не добавляя новых:
...
class Trout(Fish):
pass
terry = Trout("Terry")
print(terry.first_name + " " + terry.last_name)
print(terry.skeleton)
print(terry.eyelids)
terry.swim()
terry.swim_backwards()
Теперь в файле есть объект Trout, который использует все методы класса Fish несмотря на то, что они не объявлены в самом объекте. Нужно только передать значение “Terry” переменной first_name; все остальные переменные уже инициализированы.
Запустите программу. Вы получите:
Terry Fish
bone
False
The fish is swimming.
The fish can swim backwards.
Создайте другой дочерний класс, теперь уже добавив новые методы. Класс будет называться Clownfish, его индивидуальный метод – live_with_anemone.
...
class Clownfish(Fish):
def live_with_anemone(self):
print("The clownfish is coexisting with sea anemone.")
Создайте объект Clownfish:
...
casey = Clownfish("Casey")
print(casey.first_name + " " + casey.last_name)
casey.swim()
casey.live_with_anemone()
Запустив программу, вы получите такой вывод:
Casey Fish
The fish is swimming.
The clownfish is coexisting with sea anemone.
Как видите, объект casey использует методы __init__() и swim() родительского класса Fish и индивидуальный метод live_with_anemone().
Если попробовать использовать метод live_with_anemone() в объекте Trout, получится ошибка:
terry.live_with_anemone()
AttributeError: 'Trout' object has no attribute 'live_with_anemone'
Это потому, что метод live_with_anemone() принадлежит исключительно дочернему классу Clownfish.
Переопределение методов родительского класса
Только что вы создали дочерний класс Trout, который с помощью ключевого слова pass полностью наследует методы родительского класса Fish, и дочерний класс Clownfish, который не только наследует методы родительского класса, но и использует свой собственный метод.
Однако в некоторых случаях нужно использовать не все, а только отдельные методы родительского класса. Для этого методы родительского класса можно переопределять.
Создавая родительский и дочерний класс, важно учитывать проектирование программы, чтобы переопределение не создало ненужного или избыточного кода.
Для примера создайте дочерний класс Shark. При создании класса Fish использовался параметр skeleton=”bone”, но в случае с классом Sharkэто неверно. Переопределите этот параметр.
Примечание: Что касается проектирования программы, если бы переопределяемый параметр использовался несколькими классами, а не одним, лучше было бы создать для них отдельный родительский класс.
Переопределите метод конструктора __init__() и метод swim_backwards(). Метод swim() изменять не нужно. Дочерний класс будет выглядеть так:
...
class Shark(Fish):
def __init__(self, first_name, last_name="Shark",
skeleton="cartilage", eyelids=True):
self.first_name = first_name
self.last_name = last_name
self.skeleton = skeleton
self.eyelids = eyelids
def swim_backwards(self):
print("The shark cannot swim backwards, but can sink backwards.")
Параметры метода конструктора __init__() были переопределены; теперь переменная last_name имеет значение “Shark”, переменная skeleton имеет значение “cartilage”, а eyelids – значение True.
Метод swim_backwards() теперь выводит другое значение, а не то, которое определено в родительском классе Fish.
Теперь создайте экземпляр класса Shark, который будет использовать метод swim() родительского класса Fish.
...
wally = Shark("Wally")
print(wally.first_name + " " + wally.last_name)
wally.swim()
wally.swim_backwards()
print(wally.eyelids)
print(wally.skeleton)
Запустите этот код, и вы получите:
Wally Shark
The fish is swimming.
The shark cannot swim backwards, but can sink backwards.
True
cartilage
В дочернем классе Shark методы __init__() и swim_backwards() успешно переопределены.
Функция super()
С помощью функции super() вы можете получить доступ к унаследованным методам, которые были перезаписаны в объекте класса.
Функция super() вызывает родительский метод в дочерний и использует его. Например, это позволяет переопределить один из аспектов родительского метода, а затем вызвать остальную часть исходного родительского метода.
К примеру, в программе, которая выставляет оценки студентам, внутри родительского класса Grade может быть дочерний класс Weighted_grade. В классе Weighted_grade можно переопределить метод родительского класса calculate_grade() и унаследовать остальные методы без изменений. Для этого и нужна функция super().
Обычно функция super() используется в методе конструктора __init__(), потому что именно там, скорее всего, появятся уникальные методы дочернего класса.
Попробуйте изменить дочерний класс Trout. Добавьте переменную water в метод __init__() и присвойте ей значение “freshwater”. Остальные методы родительского класса можно наследовать без изменений.
...
class Trout(Fish):
def __init__(self, water = "freshwater"):
self.water = water
super().__init__(self)
...
Метод __init__() класса Trout был переопределён. Он иначе реализует методы __init__() родительского класса Fish. В методе __init__() класса Trout был явно инициализирован метод __init__() класса Fish.
Поскольку метод переопределён, больше не нужно передавать first_name как параметр Trout. Если бы вы передали параметр, параметр freshwater был бы сброшен. Поэтому переменную first_name нужно инициализировать путём вызова в объекте.
Теперь можно вызвать инициализированные переменные родительского класса, а также использовать уникальную дочернюю переменную.
...
terry = Trout()
# Инициализация first name
terry.first_name = "Terry"
# Использование родительского метода __init__() с помощью функции super()
print(terry.first_name + " " + terry.last_name)
print(terry.eyelids)
# Дочерний метод __init__()
print(terry.water)
# Родительский метод swim()
terry.swim()
Terry Fish
False
freshwater
The fish is swimming.
Как видите, объект terry класса Trout может использовать свой метод __init__() с переменной water и родительский метод __init__() класса Fish с переменными first_name, last_name и eyelids.
Множественное наследование
Множественное наследование подразумевает, что класс может наследовать атрибуты и методы из нескольких родительских классов одновременно. Это позволяет программам сократить избыточность, но также может усложнить код, поэтому множественное наследование нужно использовать только с учетом общей конструкции программы.
Попробуйте создать класс Coral_reef, дочерний по отношению к классам Coral и Sea_anemone. Создайте в каждом классе метод и передайте его с помощью ключевого слова pass в дочерний класс Coral_reef.
class Coral:
def community(self):
print("Coral lives in a community.")
class Anemone:
def protect_clownfish(self):
print("The anemone is protecting the clownfish.")
class CoralReef(Coral, Anemone):
pass
Класс Coral содержит метод community(), который выводит одну строку текста, а класс Anemone содержит метод protect_clownfish(), который отображает другую строку. После этого оба класса вызываются в кортеж. Таким образом класс Coral может наследовать оба родительских класса.
Читайте также: Кортежи в Python 3
Создайте объект класса Coral:
...
great_barrier = CoralReef()
great_barrier.community()
great_barrier.protect_clownfish()
Объект great_barrier класса CoralReef использует методы обоих родительских классов.
Запустите код:
Coral lives in a community.
The anemone is protecting the clownfish.
Как видите, множественное наследование работает правильно.
Множественное наследование позволяет использовать методы нескольких родительских классов внутри одного дочернего класса. Если в родительских классах присутствует один и тот же метод, дочерний класс унаследует его из того родительского класса, который идёт первым в кортеже.
Заключение
Теперь вы знакомы с основами наследования в Python 3 и умеете создавать родительские и дочерние классы, переопределять методы, использовать функцию super() и использовать множественное наследование.
Наследование в объектно-ориентированном программировании позволяет соблюдать принцип разработки DRY («don’t repeat yourself»), благодаря чему программа содержит меньше повторяющихся блоков кода. Наследование также заставляет разработчиков заранее продумывать конструкцию программы.
Tags: Python, Python 3