Порядок множественного наследования вызовов - PullRequest
5 голосов
/ 08 января 2012
 A      B
| |   |   |
C D   E   F
| |   |   |
    G     H
      |
      I


user@ubuntu:~/Documents/Python/oop_python$ cat tt.py
class A:
    def call_me(self):
        print("A")

class C(A):
    def call_me(self):
        super().call_me()
        print("C")

class D(A):
    def call_me(self):
        super().call_me()
        print("D")

class B:
    def call_me(self):
        print("B")

class E(B):
    def call_me(self):
        super().call_me()
        print("E")

class F(B):
    def call_me(self):
        super().call_me()
        print("F")

class G(C, D, E):
    def call_me(self):
        super().call_me()
        print("G")

class H(F):
    def call_me(self):
        super().call_me()
        print("H")

class I(G, H):
    def call_me(self):
        super().call_me()
        print("I")


user@ubuntu:~/Documents/Python/oop_python$ python3.2 -i tt.py
>>> i = I()
>>> i.call_me()
A
D
C
G
I

Вопрос > Почему B, E, F не печатаются?

//            updated based on comments from delnan

user@ubuntu:~/Documents/Python/oop_python$ cat tt.py
class BaseClass():
    def call_me(self):
        print("BaseClass")
    pass

class A(BaseClass):
    def call_me(self):
        super().call_me()
        print("A")

class C(A):
    def call_me(self):
        super().call_me()
        print("C")

class D(A):
    def call_me(self):
        super().call_me()
        print("D")

class B(BaseClass):
    def call_me(self):
        super().call_me()
        print("B")

class E(B):
    def call_me(self):
        super().call_me()
        print("E")

class F(B):
    def call_me(self):
        super().call_me()
        print("F")

class G(C, D, E):
    def call_me(self):
        super().call_me()
        print("G")

class H(F):
    def call_me(self):
        super().call_me()
        print("H")

class I(G, H):
    def call_me(self):
        super().call_me()
        print("I")

user@ubuntu:~/Documents/Python/oop_python$ python3.2 -i tt.py
>>> i = I()
>>> i.call_me()
BaseClass
B
F
H
E
A
D
C
G
I

Ответы [ 2 ]

6 голосов
/ 08 января 2012

Распространенным заблуждением является то, что super () будет вызывать все методы суперклассов.Я не буду.Это назовет только один из них.Какой из них автоматически рассчитывается super () в соответствии с некоторыми конкретными правилами.Иногда тот, кого он называет, не настоящий суперкласс, а родной брат.Но нет никакой гарантии, что все будут вызваны, если все классы, в свою очередь, не используют super ().

В этом случае A и B не вызывают super.И если вы добавите его в A, он на самом деле вызовет «отсутствующие» классы, но если вы добавите его в B, вы получите ошибку, потому что в этом конкретном случае B окажется «последним» (или первым,в зависимости от того, как вы это видите) класс.

Если вы хотите использовать super (), лучшим решением будет иметь общий базовый класс для A и B, который реализует call_me, но не вызывает super ().(Спасибо Делнану за предложение).

Однако, если вы знаете иерархию классов, вы можете напрямую вызывать методы суперклассов вместо использования super ().Обратите внимание, что это в приведенном выше случае не означает, что каждый класс должен вызывать каждый из своих базовых классов напрямую.Поэтому это бесполезно в тех случаях, когда вы, как программист, не имеете полного контроля над иерархией классов, например, если вы пишете библиотеки или mixin-классы.Тогда вы должны использовать super ().

1 голос
/ 16 мая 2013

Условием для использования super является класс new-style .

Существует два варианта использования super в python:

  1. Иерархия классов с единичным наследованием super может использовать для ссылки на родительский класс, не называя их явно

  2. Второй вариант использования - это поддержка множественного наследования в динамической среде выполнения.

Итак, второй случай соответствует вашему второму примеру условия. В этом случае подкласс будет выполнять bfs traverse , чтобы убедиться, что каждый метод базового класса перезаписи вызывается только один раз. Ваше дерево наследования может быть переписано в лайнер в следующем порядке (следуйте указаниям bfs , слева направо ) I G C D A E H F B BaseClass.

Определение супер-метода:

* 1 028 * супер (типа [объектно-или-тип])

Он вернет прокси-объект, который делегирует вызовы метода родительскому или родственному классу типа. В python 3.x вы можете вызывать super () напрямую, что аналогично super (currentClassName, self). Это позволит получить прокси прямого правого супер-родственного класса, сгенерированного ходом bfs. Так что метод i.call_me () будет называться:

I.call_me -> G.call_me -> C.call_me -> D.call_me -> A.call_me -> E.call_me -> H.call_me -> F.call_me -> B.call_me -> BaseClass. call_me

Разница между двумя примерами

Как вы спросили, почему в первом примере не было напечатано B, E, F, потому что карта наследования не является "графом ромба", что означает, что A.call_me B.call_me - это другой метод. Таким образом, Python выбирает только одну из ветвей наследования.

Желаю это полезно. Вы также можете проверить python class doc для более подробной информации

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