Как Python super() работает с множественным наследованием?

Я довольно новичок в объектно-ориентированном программировании на Python, и мне трудно понять super() функция (новые классы стиля), особенно когда речь идет о множественном наследовании.

Например, если у вас есть что-то вроде:

class First(object):
    def __init__(self):
        print "first"

class Second(object):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print "that's it"

Что я не понимаю, так это: Third() класс наследует оба метода конструктора? Если да, то какой из них будет работать с super() и почему?

А что, если вы хотите запустить другой? Я знаю, что это как-то связано с порядком разрешения методов Python ( MRO).

18 ответов

Решение

Это подробно описано с достаточным количеством подробностей самим Гвидо в его посте " Постановление о разрешении метода" (включая две более ранние попытки).

В вашем примере Third() позвоню First.__init__, Python ищет каждый атрибут в родительских классах, поскольку они перечислены слева направо. В этом случае мы ищем __init__, Итак, если вы определите

class Third(First, Second):
    ...

Python начнет с просмотра First, и если First не имеет атрибута, то он будет смотреть на Second,

Эта ситуация становится более сложной, когда наследование начинает пересекать пути (например, если First унаследованный от Second). Прочитайте ссылку выше для получения более подробной информации, но, в двух словах, Python будет пытаться поддерживать порядок, в котором каждый класс появляется в списке наследования, начиная с самого дочернего класса.

Так, например, если у вас было:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First):
    def __init__(self):
        print "third"

class Fourth(Second, Third):
    def __init__(self):
        super(Fourth, self).__init__()
        print "that's it"

MRO будет [Fourth, Second, Third, First].

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

Отредактировано, чтобы добавить пример неоднозначного MRO:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        print "third"

Должен ThirdMRO be [First, Second] или же [Second, First]? Нет очевидного ожидания, и Python выдаст ошибку:

TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution order (MRO) for bases Second, First

Редактировать: я вижу, что несколько человек утверждают, что в приведенных выше примерах отсутствует super() звонки, поэтому позвольте мне объяснить: цель примеров - показать, как строится MRO. Они не предназначены для печати "первая \ третья \ третья" или что-то еще. Вы можете - и должны, конечно, поиграться с примером, добавить super() вызовы, посмотрите, что происходит, и получите более глубокое понимание модели наследования Python. Но моя цель здесь состоит в том, чтобы сделать это простым и показать, как строится MRO. И это построено, как я объяснил:

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
 <class '__main__.Second'>, <class '__main__.Third'>,
 <class '__main__.First'>,
 <type 'object'>)

Ваш код и другие ответы все глючат. Они пропускают super() вызовы в первых двух классах, которые требуются для совместной работы подклассов.

Вот исправленная версия кода:

class First(object):
    def __init__(self):
        super(First, self).__init__()
        print("first")

class Second(object):
    def __init__(self):
        super(Second, self).__init__()
        print("second")

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print("third")

super() вызов находит следующий метод в MRO на каждом шаге, поэтому у First и Second он тоже должен быть, иначе выполнение останавливается в конце Second.__init__(),

Вот что я получаю:

>>> Third()
second
first
third

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

Что вам нужно понять, так это super(MyClass, self).__init__() обеспечивает следующий __init__ метод в соответствии с используемым алгоритмом Order Resolution Ordering (MRO) в контексте полной иерархии наследования.

Эта последняя часть имеет решающее значение для понимания. Давайте рассмотрим пример еще раз:

class First(object):
  def __init__(self):
    super(First, self).__init__()
    print "first"

class Second(object):
  def __init__(self):
    super(Second, self).__init__()
    print "second"

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__()
    print "that's it"

В соответствии с этой статьей о порядке разрешения постановления Гвидо ван Россума, распоряжение о разрешении __init__ рассчитывается (до Python 2.3) с использованием "обхода слева направо в глубину":

Third --> First --> object --> Second --> object

После удаления всех дубликатов, кроме последнего, получаем:

Third --> First --> Second --> object

Итак, давайте проследим, что происходит, когда мы создаем экземпляр Third класс, например x = Third(),

  1. По данным MRO __init__ Третьего называется первым.

  2. Далее, согласно MRO, внутри __init__ метод super(Third, self).__init__() решает до __init__ метод First, который вызывается.

  3. внутри __init__ из первых super(First, self).__init__() вызывает __init__ Во-вторых, потому что это то, что диктует MRO!

  4. внутри __init__ второй super(Second, self).__init__() вызывает __init__ объекта, который составляет ничто. После этого"второй" печатается.

  5. После super(First, self).__init__() завершено, "первый" печатается.

  6. После super(Third, self).__init__() завершено, "это все" печатается.

Это подробно объясняет, почему создание экземпляра Third() приводит к:

>>> x = Third()
second
first
that's it

Алгоритм MRO был улучшен с Python 2.3 и выше, чтобы хорошо работать в сложных случаях, но я предполагаю, что использование "обхода слева направо в глубину" + "удаление дубликатов, ожидающих последнего" все еще работает в большинстве случаев (пожалуйста, комментарий, если это не так). Обязательно прочитайте пост в блоге Гвидо!

Это известно как проблема с бриллиантами, на странице есть запись на Python, но вкратце Python будет вызывать методы суперкласса слева направо.

В общем и целом

Предполагая, что все происходит от object (если вы этого не сделаете), Python вычисляет порядок разрешения методов (MRO) на основе вашего дерева наследования классов. MRO удовлетворяет 3 свойствам:

  • Дети класса приходят раньше своих родителей
  • Левые родители идут раньше правых родителей
  • Класс появляется только один раз в MRO

Если такого порядка не существует, ошибки Python. Внутренняя работа этого является C3 Linerization происхождения классов. Прочитайте все об этом здесь: https://www.python.org/download/releases/2.3/mro/

Таким образом, в обоих приведенных ниже примерах это:

  1. ребенок
  2. Оставил
  3. Правильно
  4. родитель

Когда вызывается метод, первое вхождение этого метода в MRO - это тот, который вызывается. Любой класс, который не реализует этот метод, пропускается. Любой звонок в super внутри этого метода будет вызываться следующее вхождение этого метода в MRO. Следовательно, имеет значение как порядок, в котором вы размещаете классы в наследстве, так и то, куда вы помещаете вызовы. super в методах.

С super первый в каждом методе

class Parent(object):
    def __init__(self):
        super(Parent, self).__init__()
        print "parent"

class Left(Parent):
    def __init__(self):
        super(Left, self).__init__()
        print "left"

class Right(Parent):
    def __init__(self):
        super(Right, self).__init__()
        print "right"

class Child(Left, Right):
    def __init__(self):
        super(Child, self).__init__()
        print "child"

Child() Выходы:

parent
right
left
child

С super последний в каждом методе

class Parent(object):
    def __init__(self):
        print "parent"
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print "left"
        super(Left, self).__init__()

class Right(Parent):
    def __init__(self):
        print "right"
        super(Right, self).__init__()

class Child(Left, Right):
    def __init__(self):
        print "child"
        super(Child, self).__init__()

Child() Выходы:

child
left
right
parent

Я понимаю, что это не отвечает прямо super() вопрос, но я чувствую, что это достаточно важно, чтобы поделиться.

Существует также способ прямого вызова каждого унаследованного класса:


class First(object):
    def __init__(self):
        print '1'

class Second(object):
    def __init__(self):
        print '2'

class Third(First, Second):
    def __init__(self):
        Second.__init__(self)

Просто отметьте, что если вы сделаете это таким образом, вам придется звонить каждому вручную, я уверен First"s __init__() не будет звонить.

Это то, как я решил проблему множественного наследования с разными переменными для инициализации и наличия нескольких MixIns с одним и тем же вызовом функции. Я должен был явно добавить переменные к переданным **kwargs и добавить интерфейс MixIn, чтобы быть конечной точкой для супер вызовов.

Вот A является расширяемым базовым классом и B а также C классы MixIn, которые предоставляют функцию f, A а также B оба ожидают параметра v в их __init__ а также C надеется w, Функция f принимает один параметр y, Q наследует от всех трех классов. MixInF это миксин интерфейс для B а также C,


class A(object):
    def __init__(self, v, *args, **kwargs):
        print "A:init:v[{0}]".format(v)
        kwargs['v']=v
        super(A, self).__init__(*args, **kwargs)
        self.v = v


class MixInF(object):
    def __init__(self, *args, **kwargs):
        print "IObject:init"
    def f(self, y):
        print "IObject:y[{0}]".format(y)


class B(MixInF):
    def __init__(self, v, *args, **kwargs):
        print "B:init:v[{0}]".format(v)
        kwargs['v']=v
        super(B, self).__init__(*args, **kwargs)
        self.v = v
    def f(self, y):
        print "B:f:v[{0}]:y[{1}]".format(self.v, y)
        super(B, self).f(y)


class C(MixInF):
    def __init__(self, w, *args, **kwargs):
        print "C:init:w[{0}]".format(w)
        kwargs['w']=w
        super(C, self).__init__(*args, **kwargs)
        self.w = w
    def f(self, y):
        print "C:f:w[{0}]:y[{1}]".format(self.w, y)
        super(C, self).f(y)


class Q(C,B,A):
    def __init__(self, v, w):
        super(Q, self).__init__(v=v, w=w)
    def f(self, y):
        print "Q:f:y[{0}]".format(y)
        super(Q, self).f(y)

О комментарии @ Calfzhou, вы можете использовать, как обычно, **kwargs:

Пример работающего онлайн

class A(object):
  def __init__(self, a, *args, **kwargs):
    print("A", a)

class B(A):
  def __init__(self, b, *args, **kwargs):
    super(B, self).__init__(*args, **kwargs)
    print("B", b)

class A1(A):
  def __init__(self, a1, *args, **kwargs):
    super(A1, self).__init__(*args, **kwargs)
    print("A1", a1)

class B1(A1, B):
  def __init__(self, b1, *args, **kwargs):
    super(B1, self).__init__(*args, **kwargs)
    print("B1", b1)


B1(a1=6, b1=5, b="hello", a=None)

Результат:

A None
B hello
A1 6
B1 5

Вы также можете использовать их позиционно:

B1(5, 6, b="hello", a=None)

но вы должны помнить MRO, это действительно сбивает с толку.

Я могу быть немного раздражающим, но я заметил, что люди забыли каждый раз использовать *args а также **kwargs когда они переопределяют метод, в то время как это один из немногих действительно полезных и разумных способов использования этих "магических переменных".

Еще один не охваченный вопрос - передача параметров для инициализации классов. С момента назначения super В зависимости от подкласса единственный хороший способ передать параметры - это собрать их все вместе. Тогда будьте осторожны, чтобы не иметь одинаковое имя параметра с разными значениями.

Пример:

class A(object):
    def __init__(self, **kwargs):
        print('A.__init__')
        super().__init__()

class B(A):
    def __init__(self, **kwargs):
        print('B.__init__ {}'.format(kwargs['x']))
        super().__init__(**kwargs)


class C(A):
    def __init__(self, **kwargs):
        print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
        super().__init__(**kwargs)


class D(B, C): # MRO=D, B, C, A
    def __init__(self):
        print('D.__init__')
        super().__init__(a=1, b=2, x=3)

print(D.mro())
D()

дает:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__

Вызов суперкласса __init__ напрямую к более прямому назначению параметров заманчиво, но не получается, если есть super вызов в суперклассе и / или MRO изменяется, и класс A может вызываться несколько раз, в зависимости от реализации.

В заключение: кооперативное наследование, супер и специфические параметры для инициализации не очень хорошо работают вместе.

Рассмотрите возможность звонка super().Foo()вызывается из подкласса. Метод "Порядок разрешения методов" (MRO)- это порядок, в котором разрешаются вызовы методов.

Случай 1: одиночное наследование

В этом случае super().Foo() будет найден в иерархии и рассмотрит ближайшую реализацию, если она найдена, иначе вызовет исключение. Отношение "есть" всегда будет истинным между любым посещаемым подклассом и его суперклассом вверху иерархии. Но эта история не всегда одинакова для множественного наследования.

Случай 2: множественное наследование

Здесь, реализация при поиске супер ().Foo(), каждый посетил класс в иерархии может или не может иметьэто отношение. Рассмотрим следующие примеры:

class A(object): pass
class B(object): pass
class C(A): pass
class D(A): pass
class E(C, D): pass
class F(B): pass
class G(B): pass
class H(F, G): pass
class I(E, H): pass

Вот,Iэто самый низкий класс в иерархии. Схема иерархии и ТОиР дляIбудет

(Красные цифры, показывающие ТОиР)

ТОиРI E C D A H F G B object

Обратите внимание, что классXбудет посещаться только в том случае, если были посещены все его подклассы, которые наследуют от него (т. е. вы никогда не должны посещать класс, в который стрелка входит из класса ниже, который вы еще не посещали).

Здесь обратите внимание, что после посещения класса C, D посещается, хотя C а также DDO NOT естьэто отношения между ними (но оба имеют сA). Это гдеsuper()отличается от одиночного наследования.

Рассмотрим немного более сложный пример:

(Красные цифры, показывающие ТОиР)

ТОиР I E C H D A F G B object

В этом случае мы исходим из I к E к C. Следующим шагом будетA, но мы еще не побывали D, подкласс A. Мы не можем посетитьD, однако, поскольку мы еще не посетили H, подкласс D. ЛистьяHкак следующий класс для посещения. Помните, мы пытаемся подняться по иерархии, если возможно, поэтому мы посещаем его самый левый суперкласс,D. ПослеD мы посетили A, но мы не можем подойти к объекту, потому что нам еще предстоит посетить F, G, а также B. Эти классы, по порядку, завершают MRO дляI.

Обратите внимание, что ни один класс не может появляться в MRO более одного раза.

Вот как super () выглядит в иерархии наследования.

Кредиты на ресурсы: Ричард Л. Холтерман Основы программирования на Python

В случае, когда каждый класс, от которого вы пытаетесь наследовать, имеет собственные позиционные аргументы для его init, просто вызовите собственный метод init каждого класса и не используйте super при попытке наследования от нескольких объектов.

      class A():
    def __init__(self, x):
        self.x = x

class B():
    def __init__(self, y, z):
        self.y = y
        self.z = z

class C(A, B):
    def __init__(self, x, y, z):
        A.__init__(self, x)
        B.__init__(self, y, z)

>>> c = C(1,2,3)
>>>c.x, c.y, c.z 
(1,2,3)

Рассмотрим дочерний элемент, где родители и имеют аргументы ключевого слова в своих конструкторах.

        A    B
   \  /
    AB

Для инициализации вам нужно явно вызвать конструкторы родительского класса вместо использования super().

Пример:

      class A():
    def __init__(self, a="a"):
        self.a = a
        print(f"a={a}")
    
    def A_method(self):
        print(f"A_method: {self.a}")

class B():
    def __init__(self, b="b"):
        self.b = b
        print(f"b={b}")
    
    def B_method(self):
        print(f"B_method: {self.b}")
    
    def magical_AB_method(self):
        print(f"magical_AB_method: {self.a}, {self.b}")

class AB(A,B):
    def __init__(self, a="A", b="B"):
        # super().__init__(a=a, b=b) # fails!
        A.__init__(self,a=a)
        B.__init__(self,b=b)
        self.A_method()
        self.B_method()
        self.magical_AB_method()


A()
>>> a=a

B()
>>> b=b

AB()
>>> a=A
>>> b=B
>>> A_method: A
>>> B_method: B

Чтобы продемонстрировать, что двое родителей объединены в ребенка, рассмотрим magical_AB_methodопределен внутри класса. При вызове из экземпляра B, метод не работает, поскольку у него нет доступа к переменным-членам внутри. Однако при вызове из экземпляра дочернего элемента AB, этот метод работает, поскольку он унаследовал требуемую переменную-член от A.

      B().magical_AB_method()
>>> AttributeError: 'B' object has no attribute 'a'

AB().magical_AB_method()
>>> magical_AB_method: A, B

В Python 3.5+ наследование выглядит предсказуемо и очень приятно для меня. Пожалуйста, посмотрите на этот код:

class Base(object):
  def foo(self):
    print("    Base(): entering")
    print("    Base(): exiting")


class First(Base):
  def foo(self):
    print("   First(): entering Will call Second now")
    super().foo()
    print("   First(): exiting")


class Second(Base):
  def foo(self):
    print("  Second(): entering")
    super().foo()
    print("  Second(): exiting")


class Third(First, Second):
  def foo(self):
    print(" Third(): entering")
    super().foo()
    print(" Third(): exiting")


class Fourth(Third):
  def foo(self):
    print("Fourth(): entering")
    super().foo()
    print("Fourth(): exiting")

Fourth().foo()
print(Fourth.__mro__)

Выходы:

Fourth(): entering
 Third(): entering
   First(): entering Will call Second now
  Second(): entering
    Base(): entering
    Base(): exiting
  Second(): exiting
   First(): exiting
 Third(): exiting
Fourth(): exiting
(<class '__main__.Fourth'>, <class '__main__.Third'>, <class '__main__.First'>, <class '__main__.Second'>, <class '__main__.Base'>, <class 'object'>)

Как видите, он вызывает foo ровно ОДИН раз для каждой унаследованной цепочки в том же порядке, в котором она была унаследована. Вы можете получить этот заказ по телефону .MR:

Четвертый -> Третий -> Первый -> Второй -> База -> объект

class First(object):
  def __init__(self, a):
    print "first", a
    super(First, self).__init__(20)

class Second(object):
  def __init__(self, a):
    print "second", a
    super(Second, self).__init__()

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__(10)
    print "that's it"

t = Third()

Выход

first 10
second 20
that's it

Call to Third() определяет местоположение инициализации, определенной в Third. И вызов super в этой процедуре вызывает init, определенный в First. MRO=[Первый, Второй]. Теперь вызов super в init, заданном в First, продолжит поиск MRO и найдет init, определенный в Second, и любой вызов super попадет в объект init по умолчанию. Я надеюсь, что этот пример проясняет концепцию.

Если вы не позвоните супер из Первого. Цепь останавливается, и вы получите следующий вывод.

first 10
that's it

Я хотел бы добавить к тому, что @Visionscaper говорит вверху:

Third --> First --> object --> Second --> object

В этом случае интерпретатор не отфильтровывает объектный класс, потому что он дублирован, а скорее потому, что Second появляется в позиции головы и не появляется в хвостовой позиции в подмножестве иерархии. Пока объект появляется только в хвостовых позициях и не считается сильной позицией в алгоритме C3 для определения приоритета.

Линеаризация (MRO) класса C, L(C), является

  • Класс С
  • плюс слияние
    • линеаризация его родителей P1, P2, .. = L(P1, P2, ...) и
    • список его родителей P1, P2,..

Линейное слияние выполняется путем выбора общих классов, которые появляются в качестве заголовка списков, а не хвоста, поскольку порядок имеет значение (станет ясно ниже)

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

    L(O)  := [O]  // the linearization(mro) of O(object), because O has no parents

    L(First)  :=  [First] + merge(L(O), [O])
               =  [First] + merge([O], [O])
               =  [First, O]

    // Similarly, 
    L(Second)  := [Second, O]

    L(Third)   := [Third] + merge(L(First), L(Second), [First, Second])
                = [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2, 
                = [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
                = [Third, First, Second] + merge([O], [O])            
                = [Third, First, Second, O]

Таким образом, для реализации super() в следующем коде:

class First(object):
  def __init__(self):
    super(First, self).__init__()
    print "first"

class Second(object):
  def __init__(self):
    super(Second, self).__init__()
    print "second"

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__()
    print "that's it"

становится очевидным, как этот метод будет решен

Third.__init__() ---> First.__init__() ---> Second.__init__() ---> 
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"

В learningpyhonthehardway я изучаю нечто, называемое super() встроенной функцией, если не ошибаюсь. Вызов функции super() может помочь наследованию пройти через родителя и "родных братьев и сестер" и помочь вам увидеть яснее. Я все еще новичок, но я люблю делиться своим опытом использования этого super() в python2.7.

Если вы прочитали комментарии на этой странице, вы услышите о Порядке разрешения методов (MRO), метод, который является функцией, которую вы написали, MRO будет использовать схему Depth-First-Left-to-Right для поиска и запуска. Вы можете сделать больше исследований по этому вопросу.

Добавляя функцию super()

super(First, self).__init__() #example for class First.

Вы можете соединить несколько экземпляров и "семейств" с помощью super(), добавив каждый из них. И он выполнит методы, пройдет через них и убедится, что вы не пропустили! Однако добавление их до или после имеет значение, вы узнаете, выполнили ли вы упражнение learningpythonthehardway 44. Пусть начнется веселье!!

Принимая пример ниже, вы можете скопировать и вставить и попробовать запустить его:

class First(object):
    def __init__(self):

        print("first")

class Second(First):
    def __init__(self):
        print("second (before)")
        super(Second, self).__init__()
        print("second (after)")

class Third(First):
    def __init__(self):
        print("third (before)")
        super(Third, self).__init__()
        print("third (after)")


class Fourth(First):
    def __init__(self):
        print("fourth (before)")
        super(Fourth, self).__init__()
        print("fourth (after)")


class Fifth(Second, Third, Fourth):
    def __init__(self):
        print("fifth (before)")
        super(Fifth, self).__init__()
        print("fifth (after)")

Fifth()

Как это работает? Экземпляр пятого () будет следующим образом. Каждый шаг идет от класса к классу, где добавлена ​​супер функция.

1.) print("fifth (before)")
2.) super()>[Second, Third, Fourth] (Left to right)
3.) print("second (before)")
4.) super()> First (First is the Parent which inherit from object)

Родитель был найден, и он будет продолжаться до третьего и четвертого!!

5.) print("third (before)")
6.) super()> First (Parent class)
7.) print ("Fourth (before)")
8.) super()> First (Parent class)

Теперь все классы с super() были доступны! Родительский класс был найден и выполнен, и теперь он продолжает распаковывать функцию в наследованиях для завершения кодов.

9.) print("first") (Parent)
10.) print ("Fourth (after)") (Class Fourth un-box)
11.) print("third (after)") (Class Third un-box)
12.) print("second (after)") (Class Second un-box)
13.) print("fifth (after)") (Class Fifth un-box)
14.) Fifth() executed

Результат программы выше:

fifth (before)
second (before
third (before)
fourth (before)
first
fourth (after)
third (after)
second (after)
fifth (after)

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

Отправляю этот ответ для моей будущей ссылки.

Множественное наследование Python должно использовать модель ромба, и подпись функции не должна изменяться в модели.

    A
   / \
  B   C
   \ /
    D

Фрагмент кода примера будет: -

class A:
    def __init__(self, name=None):
        #  this is the head of the diamond, no need to call super() here
        self.name = name

class B(A):
    def __init__(self, param1='hello', **kwargs):
        super().__init__(**kwargs)
        self.param1 = param1

class C(A):
    def __init__(self, param2='bye', **kwargs):
        super().__init__(**kwargs)
        self.param2 = param2

class D(B, C):
    def __init__(self, works='fine', **kwargs):
        super().__init__(**kwargs)
        print(f"{works=}, {self.param1=}, {self.param2=}, {self.name=}")

d = D(name='Testing')

Здесь класс А object

Может быть, есть еще что-то, что можно добавить, небольшой пример с Django rest_framework и декораторами. Это дает ответ на неявный вопрос: "зачем мне это вообще?"

Как уже было сказано: мы используем Django rest_framework, и мы используем общие представления, и для каждого типа объектов в нашей базе данных мы обнаруживаем, что один класс представления предоставляет GET и POST для списков объектов, а другой класс представления предоставляет GET., PUT и DELETE для отдельных объектов.

Теперь POST, PUT и DELETE, которые мы хотим украсить, добавив логин_required Django. Обратите внимание, как это касается обоих классов, но не всех методов в каждом классе.

Решение могло пройти через множественное наследование.

from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required

class LoginToPost:
    @method_decorator(login_required)
    def post(self, arg, *args, **kwargs):
        super().post(arg, *args, **kwargs)

То же самое и для других методов.

В список наследования моих конкретных классов я бы добавил свой LoginToPost перед ListCreateAPIView а также LoginToPutOrDelete перед RetrieveUpdateDestroyAPIView. Мои конкретные занятия 'get останется без украшений.

Другие вопросы по тегам