I wrote classes with diamond inheritance. Were there are two classes at same level the constructors have different length of arguments list and depending on declaration order in list of bases declaration everything works fine or error in thrown immediately
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
Constructors are called in order:
DFT
;
AnalysisResultSaver
;
Signal
;
Base
;
In this case everything works fine but my question is
1) how are arguments passed to Signal
constructor if there's no direct indication which arguments are right ones, is it just trimmed to first two?
But if I change order of bases in DFT then I get
super().__init__(fs, N, history_len, strict, np.complex)
TypeError: __init__() takes from 3 to 4 positional arguments but 6 were given
I know it changes mro but in first case it works fine
and if I want to call constructors directly by Common.BaseAS.__init__()
and Common.Signal.__init__()
than Signal constructor is called twice so somehow call to BaseAS calls Signal constructor even though it's not its parent.
Common.BaseAS.__init__(self, fs, N, history_len, strict, np.complex)
Common.Signal.__init__(self, fs, N)
So 2) how can BaseAS
call Signal
constuctor?
The answer by @KSab is correct, but I will add this as it helps illustrate what is happening (and was suggested in that answer). I modified your code a bit to show exactly what is happening and in what order as these objects are constructed. Here is the code:
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)
It will produce this output:
================================================================================
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'>
================================================================================
Also, this is the MRO for each class:
>>> 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'>]