Skip to content

Latest commit

 

History

History
239 lines (180 loc) · 9.16 KB

File metadata and controls

239 lines (180 loc) · 9.16 KB

Наследование

Все классы в питоне наследуются от базового object во 2 версии это делалось явно, начиная с 3 версии это происходит автоматически.

Есть класс class Prop чтобы наследоваться от него, указываем его в дочернем классе, в скобках class Line(Prop)

Когда в классе Point создается метод конструктор:

    class Prop:
        def __init__(self, start, end, color, width):
            print("Метод __init__ класса Prop")
            self._start = start
            self._end = end
            self._color = color
            self._width = width

Если мы наследуемся от него классом Line, и в самом классе Line мы не создаем метода конструктора, то по дефолту будет использоваться метод конструктор родительского класса:

     class Line(Prop):
        def drawLine(self):
            print(f"Рисуем линию: {self._start}, {self._end}," \
                   f" {self._color}, {self._width}")

Если мы хотим переопределить метод конструктор в Line, или хотим добавить дополнительный функционал, то мы можем сделать это 2 способами:

  1. Point.__init__(self, *args) Этот метод не правильный, его можно использовать, но указание конкретного класса родителя приводит к путанице в случае множественного наследования.

Обратим внимание, что данный способ вызова требует передачу self

  1. super().__init__(*args) Этот метод правильный, он сам извлекает из стека наследуемых классов правильный.

Обратим внимание, что данный способ вызова не требует передачу self


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

Этот механизм я встречал только в 2 языках, C++ и Python.

Суть в том что мы можем наследоваться сразу от 2 классов.

Указание происходит также в скобках, первым вызывается методы класса который указан первым, есть в данном случае первый будет вызван One:

class My(One, Two):

При вызове конструктора, будет вызван конструктор класса One если конечно нет конструктора в классе My

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


Реализация без super()

    class Poss:
        def __init__(self, x, y, *args):
            self._x = x
            self._y = y
            Style.__init__(self, *args)

    class Style:
        def __init__(self, name, style):
            self._name = name
            self._style = style

    class Point(Poss, Style):
        def draw(self):
            return f"x={self._x} y={self._y} name={self._name} style={self._style}"

    pt = Point(10, 20, 'Название', 'Стили')
    print(pt.draw())

    # Вывод
    # x=10 y=20 name=Название style=Стили

Тут проблема заключается в том что указывая в явном виде эту строчку Style.__init__(self, *args) мы жестко задаем класс конструктор которого будет вызван.

А если в классе Style указать что то типа Poss.__init__(self, *args) то это приведет нас к рекурсии, в общем лучше так не делать.


Реализация с super()

Мы могли бы сделать такую реализацию:

    class Styles:
        def __init__(self, color="red", width=1, *args):
            print("Конструктор Styles")
            self._color = color
            self._width = width
            super().__init__(*args)

    class Pos:
        def __init__(self, sp:Point, ep:Point, *args):
            print("Конструктор Pos")
            self._sp = sp
            self._ep = ep
            super().__init__(*args)

    class Line(Pos, Styles):
        def draw(self):
            print(f"Рисование линии: {self._sp}, {self._ep}, {self._color}, {self._width}")

То есть просто определять в каждом конструкторе super().__init__(*args) который бы устанавливал нужные атрибуты, а другие передавал бы далее по цепочке родителей.

Но в таком случае есть другая проблема, нам бы потребовалось четко контролировать порядок передачи аргументов, и мы бы не могли менять порядок наследования.

Правильная реализация выглядит следующим образом:

    class Styles:
        def __init__(self):
            print("Конструктор Styles")
            super().__init__()

    class Pos:
        def __init__(self):
            print("Конструктор Pos")
            super().__init__()

    class Line(Pos, Styles):
    # class Line(Styles, Pos):
        def __init__(self, sp:Point, ep:Point,color="red", width=1,):
            self._sp = sp
            self._ep = ep
            self._color = color
            self._width = width

        def draw(self):
            print(f"Рисование линии: {self._sp}, {self._ep}, {self._color}, {self._width}")

    ln = Line(Point(10, 10), Point(100, 100), "green", 5)
    ln.draw()

Как можно увидеть тут вся инициализация происходит только в непосредственном родительском классе, а не в его родителях, родитель только вызывают super().__init__() более верхнего класса.

При такой реализации нпм уже не важно какой порядок наследования, он может быть любым.


C3 алгоритм наследования MRO

Порядок наследования называется MRO он указывает порядок наследования классов с начала Line -> Pos -> Style -> object

Это можно увидеть при помощи спец атрибута класса __mro__

    print(Line.__mro__)

    # Вывод
    # (
    #     <class '__main__.func5.<locals>.Line'>,
    #     <class '__main__.func5.<locals>.Pos'>, 
    #     <class '__main__.func5.<locals>.Styles'>, 
    #     <class 'object'>
    # )

Способ вызова нужного количества аргументов

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

Сделать это можно путем обь единения и разделения параметров с помощью символа *

Пример работы:

    """Пример разделения аргументов на части"""
    class One:
        def __init__(self, x, y):
            print('One = ', x, ' ', y)
            self._x = x
            self._y = y

    class Two(One):
        def __init__(self, string, number, *args):
            print('Two = ', args)
            self._string = string
            self._number = number
            super().__init__(*args)

        def show(self):
            return f"x = {self._x} y = {self._y} str = '{self._string}' num = {self._number}"

    my = Two('some string', 55, 10, 20)
    print(my.show())

    # Вывод
    # Two =  (10, 20)
    # One =  10   20
    # x = 10 y = 20 str = 'some string' num = 55

Видим что используя оператор * мы можем отделять сати параметров.