Как Python super () работает с множественным наследованием? - PullRequest
761 голосов
/ 19 июля 2010

Я довольно новичок в объектно-ориентированном программировании на 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 ).

Ответы [ 13 ]

3 голосов
/ 13 ноября 2018

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

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

Добавляя функцию 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()

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

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 будет выполнять мое кодирование, и убедиться, что наследование может получить доступ к методу, который я намеревался.

2 голосов
/ 18 сентября 2016

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

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

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

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

  • Класс 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"
1 голос
/ 07 мая 2019

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

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

Теперь POST, PUT и DELETE мы хотим украсить с помощью login_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 останутся без декорации.

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