Как Python передает __init__ параметры с множественным наследованием - PullRequest
0 голосов
/ 16 октября 2018

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

class A:
    def __init__( self, x ):
        print( "A:" + x )


class B( A ):
    def __init__( self, x ):
        print( "B:" + x )
        super().__init__( "b" )


class C( A ):
    def __init__( self, x ):
        print( "C:" + x )
        super().__init__( "c" )


class D( B, C ):
    def __init__( self ):
        super().__init__( "d" )


d = D()

Вывод:

B:d
C:b
A:c
  • B:d имеет смысл, поскольку D выводитот B.
  • A:c Я почти получаю, хотя могу видеть A:b.
  • Однако бит C:b не имеет смысла: Cне выводится из B.

Может кто-нибудь объяснить?

Такие вопросы, как this , к сожалению, не упоминают параметры.

Ответы [ 3 ]

0 голосов
/ 16 октября 2018

Python использует алгоритм линеаризации C3 , чтобы установить порядок разрешения метода, который совпадает с порядком, в котором делегируется super.

По сути, алгоритм сохраняет списки для каждого класса, содержащегоэтот класс и каждый класс, от которого он наследуется, для всех классов, от которых унаследован данный класс.Затем он создает порядок классов, беря классы, которые не наследуются никакими неисследованными классами, один за другим, пока не достигнет корня, object.Ниже я использую O для object для краткости:

L(O) = [O]

L(A) = [A] + merge(L(O), [O]) = [A, O]

L(B) = [B] + merge(L(A), [A]) = [B] + merge([A, O], [A]) = [B, A] + merge([O]) 
     = [B, A, O]

L(C) = [C] + merge(L(A), [A]) = [C] + merge([A, O], [A]) = [C, A] + merge([O]) 
     = [C, A, O]

L(D) = [D] + merge(L(B), L(C), [B, C]) = [D] + merge([B, A, O], [C, A, O], [B, C])
     = [D, B] + merge([A, O], [C, A, O], [C]) = [D, B, C] + merge([A, O], [A, O])
     = [D, B, C, A, O]
0 голосов
/ 16 октября 2018

Классы в Python составляются динамически - это включает наследование.

Вывод C:b не подразумевает, что B магически наследуется от C.Если вы создаете экземпляр B или C, никто не знает о другом.

>>> B('root')
B:root
A:b

Однако, D знает о B и C:

class D(B,C):
    ...

Существует множество технических моментов доступно на этом.Однако в основном это состоит из двух частей:

  1. Прямые базовые классы разрешаются в порядке их появления.
    • B предшествует C.
  2. Рекурсивные базовые классы разрешены не для дублирования.
    • Базовый класс как B, так и C должен следовать за обоими.

Для класса D это означает, что базовые классы разрешаюткак B->C->A!C проскользнул между B и A - но только для класса D, а не для класса B.


Обратите внимание, что на самом деле другой задействованный класс: по умолчанию все классы наследуются от object.

>>> D.__mro__
(__main__.D, __main__.B, __main__.C, __main__.A, object)

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

Это действительный для обоих B и C предшествует B и C, поскольку оба являются подклассами A.B->C->A->object не нарушает того, что B ожидает, что его суперкласс будет иметь тип A.

Со всеми другими комбинациями одна заканчивается с C предшествующим ничему (недействительным) или object предшествующимчто-то (неверно).Это исключает разрешение по глубине B->A->object->C и дублирует B->A->object->C->A->object.


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

Есть хороший пример того, как регистратор для доступа к словарю может принимать как dict, так и OrderedDict.

# basic Logger working on ``dict``
class LoggingDict(dict):
    def __setitem__(self, key, value):
        logging.info('Settingto %r' % (key, value))
        super().__setitem__(key, value)

# mixin of different ``dict`` subclass
class LoggingOD(LoggingDict, collections.OrderedDict):
    pass
0 голосов
/ 16 октября 2018

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

 >>> D.mro()
 [__main__.D, __main__.B, __main__.C, __main__.A, object]

Как вы можете видеть, если все делают правильные вещи (то есть вызывают super), MRO будет первым родителем, 2-й родитель, 1-й родительский родитель и так далее ...

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

В этом случае B и C имеют одного и того же родителя A, и A не вызывает super

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