Конструкторы суперклассов с различными списками вызовов аргументов в Python - PullRequest
2 голосов
/ 21 марта 2020

Я написал классы с наследованием бриллиантов. При наличии двух классов на одном уровне конструкторы имеют разную длину списка аргументов, и в зависимости от порядка объявления в списке баз данных все работает нормально или сразу выдается ошибка

class DFT(Common.BaseAS, Common.Signal):

    def __init__(self, fs, N, history_len=1, strict=False):

        super().__init__(fs, N, history_len, strict, np.complex)


class BaseAS(abc.ABC, AnalysisResultSaver):
   No constructor here

class AnalysisResultSaver(Base):

    def __init__(self, fs=8000, N=250, history_len=1, strict=False, dtype=None):

        super().__init__(fs, N, dtype)

class Signal(Base):

    def __init__(self, fs, N, dtype=None):
        super().__init__(fs, N, dtype)

class Base:

    def __init__(self, fs, N, dtype=None):
       Stuff  

Конструкторы вызываются по порядку: DFT; AnalysisResultSaver; Signal; Base;

В этом случае все работает нормально, но мой вопрос: 1) как аргументы передаются конструктору Signal, если нет прямого указания, какие аргументы являются правильными, это просто обрезается до первых двух?

Но если я изменю порядок баз в DFT, то получу

super().__init__(fs, N, history_len, strict, np.complex)
TypeError: __init__() takes from 3 to 4 positional arguments but 6 were given

Я знаю, что это меняет mro, но в первом случае он отлично работает

, и если я хочу Вызовите конструкторы непосредственно на Common.BaseAS.__init__() и Common.Signal.__init__(), чем конструктор Сигнала, вызывается дважды, так что каким-то образом вызов BaseAS вызывает конструктор Сигнала, даже если он не является его родителем.

Common.BaseAS.__init__(self, fs, N, history_len, strict, np.complex)
Common.Signal.__init__(self, fs, N)

Так 2) как можно BaseAS вызвать Signal конструктор?

Ответы [ 2 ]

3 голосов
/ 21 марта 2020

В вашем примере супер-вызов в AnalysisResultSaver.__init__ - это то, что вызывает Signal.__init__. Это может быть нелогично, поскольку Signal не является суперклассом AnalysisResultSaver и является примером интересного способа множественного наследования, а функция super работает в Python.

В частности, когда вы пишете super() в AnalysisResultSaver, это действительно сокращение для super(AnalysisResultSaver, self). Так что же это на самом деле делает? Он просматривает порядок разрешения методов для фактического экземпляра, который вы передали (self), и пытается найти первый соответствующий метод после класса, который вы передали (AnalysisResultSaver).

Если вы напечатаете self.__class__ в AnalysisResultSaver, как и следовало ожидать, вы увидите, что сам объект является экземпляром DFT. Если вы посмотрите на порядок разрешения методов для этого класса self.__class__.__mro__ или просто DFT.__mro__, вы увидите список классов: DFT, BaseAS, AnalysisResultSaver, Signal, Base, object.

Обратите внимание на то, как первый после AnalysisResultSaver равен Signal, так как он решает, что Signal.__init__ является специфицированным c конструктором, который он должен вызывать следующим.

Если вам интересно, я предлагаю вам прочитать подробнее о множественном наследовании Python и функции super; Есть много ресурсов в Интернете, которые охватывают эту топику c более тщательно.

2 голосов
/ 21 марта 2020

Ответ @KSab правильный, но я добавлю его, поскольку он помогает проиллюстрировать происходящее (и было предложено в этом ответе). Я немного изменил ваш код, чтобы точно показать, что происходит и в каком порядке эти объекты создаются. Вот код:

import abc
import numpy as np

class Base:
    def __init__(self, fs, N, dtype=None):
        print('='*80)
        print(f"Base fs: {fs}")
        print(f"Base N: {N}")
        print(f"Base dtype: {dtype}")


class Signal(Base):
    def __init__(self, fs, N, dtype=None):
        print('='*80)
        print(f"Signal self: {self}")
        print(f"Signal fs: {fs}")
        print(f"Signal N: {N}")
        print(f"Signal dtype: {dtype}")
        print("Signal(Base) will now call:  super().__init__(fs, N, dtype)")
        super().__init__(fs, N, dtype)


class AnalysisResultSaver(Base):
    def __init__(self, fs=8000, N=250, history_len=1, strict=False, dtype=None):
        print('='*80)
        print(f"ARS self: {self}")
        print(f"ARS fs:{fs} ")
        print(f"ARS N: {N}")
        print(f"ARS history_len: {history_len}")
        print(f"ARS strict: {strict}")
        print(f"ARS dtype: {dtype}")
        print("ARS(Base) will now call:  super().__init__(fs, N, dtype)")
        super().__init__(fs, N, dtype)

class BaseAS(abc.ABC, AnalysisResultSaver):
    pass

class DFT(BaseAS, Signal):
    def __init__(self, fs, N, history_len=1, strict=False):
        print('='*80)
        print(f"DFT self: {self}")
        print(f"DFT fs:{fs} ")
        print(f"DFT N: {N}")
        print(f"DFT history_len: {history_len}")
        print(f"DFT strict: {strict}")
        print("DFT(BaseAS, Signal) will now call: super().__init__(fs, N, history_len, strict, np.complex)")
        super().__init__(fs, N, history_len, strict, np.complex)



my_d = DFT('fs', 32, 10, True)

Он выдаст такой вывод:

================================================================================
DFT self: <__main__.DFT object at 0x10cabe310>
DFT fs:fs
DFT N: 32
DFT history_len: 10
DFT strict: True
DFT(BaseAS, Signal) will now call: super().__init__(fs, N, history_len, strict, np.complex)
================================================================================
ARS self: <__main__.DFT object at 0x10cabe310>
ARS fs:fs
ARS N: 32
ARS history_len: 10
ARS strict: True
ARS dtype: <class 'complex'>
ARS(Base) will now call:  super().__init__(fs, N, dtype)
================================================================================
Signal self: <__main__.DFT object at 0x10cabe310>
Signal fs: fs
Signal N: 32
Signal dtype: <class 'complex'>
Signal(Base) will now call:  super().__init__(fs, N, dtype)
================================================================================
Base fs: fs
Base N: 32
Base dtype: <class 'complex'>
================================================================================

Кроме того, это MRO для каждого класса:

>>> DFT.mro()
[<class '__main__.DFT'>, <class '__main__.BaseAS'>, <class 'abc.ABC'>, <class '__main__.AnalysisResultSaver'>, <class '__main__.Signal'>, <class '__main__.Base'>, <class 'object'>]
>>> BaseAS.mro()
[<class '__main__.BaseAS'>, <class 'abc.ABC'>, <class '__main__.AnalysisResultSaver'>, <class '__main__.Base'>, <class 'object'>]
>>> AnalysisResultSaver.mro()
[<class '__main__.AnalysisResultSaver'>, <class '__main__.Base'>, <class 'object'>]
>>> Signal.mro()
[<class '__main__.Signal'>, <class '__main__.Base'>, <class 'object'>]
>>> Base.mro()
[<class '__main__.Base'>, <class 'object'>]
...