ООП. Наследование.

03 января 2020



Наследование - позволяет описать новый класс на основе уже существующего.
Дочерний класс содержит все атрибуты и методы родительского класса.
Атрибуты и методы родительского класса могут быть переопределены в дочернем классе и/или добавлены новые.

Родительский класс помещается в скобки после имени класса:

class MyClass(MainClass):

Рассмотрим синтаксис и свойства наследования на примерах:

# Создадим родительский класс
class A:
    def prt_a(self):
        print("class A")

# Дочерний класс
class B(A):
    def prt_b(self):
        print("class B")

b = B()
b.prt_a() # class A
b.prt_b() # class B

В данном примере мы создали родительский класс A и дочерний класс В, в который мы передаем аргументом родительский класс A. Для каждого класса у нас определены различные методы. Далее мы инициализируем класс B b = B() и можем из него вызывать методы класса A.

Данный подход очень полезен, когда можно использовать уже готовый класс и переназначить в нем методы или дополнить своими, адаптируя его под наши нужды, при этом не меняя логики работы родительского класса, который может использоваться в другом месте программы.

Множественно наследование

Множественное наследование позволяет наследовать в Ваш класс сразу от нескольких классов:

class Base1:
    def basemethod(self):
        return "Hello"

class Base2:
    value = 44

class MyClass(Base1, Base2):
    myClassData = 10

obj = MyClass()
print(obj.myClassData)
print(obj.value)
print(obj.basemethod())

Как видим в данном примере наш класс MyClass наследует сразу от нескольких классов.

Минусы наследования (Антипаттерн ООП)

Наследование очень удобный инструмент в разработке, он позволяет сэкономить много времени и избежать дублирование кода в программе.
Но в этом же кроются минусы беспорядочного наследования. Может возникнуть ситуация когда станет сложно разбираться во всех этих многоуровневых наследования. Небольшой пример:

class Base1:
    baseData = "BASE"
    def basemethod(self):
        return "Hello"

class Base2:
    value = 44

class MyClass(Base1, Base2):
    myClassData = 10
    def basemethod(self):
        print("22")

class MyNextClass (MyClass):
    value =10
    myClassData = 5

class NextClass(MyNextClass):
    def basemethod(self):
        print("11")

class FullClass(NextClass):
    newData = "new"


obj = FullClass()

print(obj.baseData) # ?
print(obj.myClassData) # ?
print(obj.value) # ?

Понять что именно будет выведено на экран, просто взглянув на код, уже не получится. Приходится выяснять от чего наследует Ваш класс, а потом от чего наследует класс от которого наследуете Вы и т.д…. А если родительские классы расположены не рядом а в других файлах или модулях, то их структура сложнее нашего маленького примера. Все еще больше усложняется, когда приходится разбирать такие завалы в чужом коде. В таких случаях наследование может превратиться из достоинства в недостаток. Как пример, Google в языке Golang вообще отказались от наследования.