Как вызвать переопределенные методы из обоих родительских классов? - PullRequest
1 голос
/ 10 апреля 2020

Рассмотрим следующие классы, где P1 и P2 являются родительскими классами C:

class P1:
    def f(self):
        print('P1')


class P2:
    def f(self):
        print('P2')


class C(P1, P2):
    def f(self):
        print('C')
        # super().f()
        # super(P1, self).f()

c = C()
c.f()

Когда я запускаю это, он печатает C.

Если я раскомментирую первую строку, super().f(), тогда она также напечатает P1
, потому что super() вызовет метод от прямого родителя, P1

И если я раскомментирую вторую строку, super(P1, self).f(), то она также напечатает P2, потому что super(P1, self) вызовет метод у брата P1, P2

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

Или есть ли другие способы сделать это без использования функции super?

Ответы [ 2 ]

3 голосов
/ 10 апреля 2020

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

class GP: # grandparent base class
    def f(self):
        pass  # do nothing

class P1(GP):
    def f(self):
        print("P1")
        super().f()

class P2(GP):
    def f(self):
        print("P2")
        super().f()

class C(P1, P2):
    def f(self):
        print("C")
        super().f()

Это работает, потому что super точно не означает "мой родительский класс". Это означает «следующий в MRO» для этого объекта ». MRO - это Порядок разрешения методов, в основном это порядок поиска вещей в наследстве. Когда super() вызывается в C, он находит метод в P1. Но когда super() вызывается в P1 (для экземпляра C), он вызывает P2, поскольку MRO для C экземпляра равно [C, P1, P2, GP, object]. P2 идет после P1, поэтому он выбирается вторым вызовом super.

Класс GP необходим для завершения цепочки вызовов super. Если у вас его нет, последний вызов super преобразуется в object (то есть root всех деревьев наследования), и, поскольку такого метода нет, вы получите ошибку. Реализация в базовом классе не должна ничего делать, просто не нужно вызывать super в конце.

1 голос
/ 10 апреля 2020

Super вызывает следующий метод из порядка разрешения методов (MRO) производного класса.

Каждая реализация метода f должна вызывать super, родительские классы не должны знать друг о друге, super автоматически вызовет следующий метод в MRO.

Редактировать: Я забыл, что последний класс в mro всегда является объектом. У объекта нет метода с именем f. поэтому вам следует позаботиться о том, чтобы последний класс в mro, который имеет этот метод, либо не вызывал super (). f, либо перехватывал AttributeError.

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

Вы можете проверить MRO класса по атрибуту __mro__.

Этот ответ в основном основан на разговоре супер, считаемом супер Раймонд Хеттингер

class P1:
    def f(self):
        super().f()
        print('P1')

class P2:
    def f(self):
        print('P2')

class C(P1, P2):
    def f(self):
        super().f()
        print('C')

class C2(P2, P1):
    def f(self):
        super().f()
        print('C2')

>>> C().f()
P2
P1
C
>>> C2().f()
P2
C2
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.P1'>, <class '__main__.P2'>, <type 'object'>)
>>> C2.__mro__
(<class '__main__.C2'>, <class '__main__.P2'>, <class '__main__.P1'>, <type 'object'>)
...