具有不同参数列表的超类构造函数在 Python 中调用
Superclasses' constructors with different lists of arguments calls in Python
我用钻石继承写了类。如果在同一级别有两个 类,则构造函数具有不同长度的参数列表,并且根据基声明列表中的声明顺序,一切正常或立即抛出错误
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__()
比 Signal 构造函数被调用了两次,所以以某种方式调用 BaseAS 调用 Signal 构造函数,即使它不是它的父级。
Common.BaseAS.__init__(self, fs, N, history_len, strict, np.complex)
Common.Signal.__init__(self, fs, N)
所以 2) BaseAS
如何调用 Signal
构造函数?
在您的示例中,AnalysisResultSaver.__init__
中的超级调用就是调用 Signal.__init__
的内容。这可能违反直觉,因为 Signal
不是 AnalysisResultSaver
的超 class,并且是多重继承和 super
函数在 [=47= 中工作的有趣方式的示例].
特别是,当您在 AnalysisResultSaver
中编写 super()
时,这实际上是 super(AnalysisResultSaver, self)
的 shorthand。那么这实际上是做什么的呢?它查看您传入的实际实例的方法解析顺序 (self
) 并尝试在您传入的 class 之后找到第一个匹配方法 (AnalysisResultSaver
).
如果您按预期在 AnalysisResultSaver
中打印 self.__class__
,您会看到该对象本身是 DFT
的一个实例。如果您查看 class self.__class__.__mro__
或 DFT.__mro__
上的方法解析顺序,您将看到 classes 的列表:DFT
、BaseAS
, AnalysisResultSaver
, Signal
, Base
, object
.
注意 AnalysisResultSaver
之后的第一个是 Signal
,这就是它如何决定 Signal.__init__
是它接下来应该调用的特定构造函数的方式。
如果您有兴趣,我建议您阅读更多关于 Python 的多重继承和 super
函数的内容;网上有很多资源更全面地介绍了这个主题。
@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'>
================================================================================
此外,这是每个 class:
的 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'>]
我用钻石继承写了类。如果在同一级别有两个 类,则构造函数具有不同长度的参数列表,并且根据基声明列表中的声明顺序,一切正常或立即抛出错误
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__()
比 Signal 构造函数被调用了两次,所以以某种方式调用 BaseAS 调用 Signal 构造函数,即使它不是它的父级。
Common.BaseAS.__init__(self, fs, N, history_len, strict, np.complex)
Common.Signal.__init__(self, fs, N)
所以 2) BaseAS
如何调用 Signal
构造函数?
在您的示例中,AnalysisResultSaver.__init__
中的超级调用就是调用 Signal.__init__
的内容。这可能违反直觉,因为 Signal
不是 AnalysisResultSaver
的超 class,并且是多重继承和 super
函数在 [=47= 中工作的有趣方式的示例].
特别是,当您在 AnalysisResultSaver
中编写 super()
时,这实际上是 super(AnalysisResultSaver, self)
的 shorthand。那么这实际上是做什么的呢?它查看您传入的实际实例的方法解析顺序 (self
) 并尝试在您传入的 class 之后找到第一个匹配方法 (AnalysisResultSaver
).
如果您按预期在 AnalysisResultSaver
中打印 self.__class__
,您会看到该对象本身是 DFT
的一个实例。如果您查看 class self.__class__.__mro__
或 DFT.__mro__
上的方法解析顺序,您将看到 classes 的列表:DFT
、BaseAS
, AnalysisResultSaver
, Signal
, Base
, object
.
注意 AnalysisResultSaver
之后的第一个是 Signal
,这就是它如何决定 Signal.__init__
是它接下来应该调用的特定构造函数的方式。
如果您有兴趣,我建议您阅读更多关于 Python 的多重继承和 super
函数的内容;网上有很多资源更全面地介绍了这个主题。
@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'>
================================================================================
此外,这是每个 class:
的 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'>]