【问题标题】:Python Class Layered Inheritence QueryPython类分层继承查询
【发布时间】:2020-01-14 18:23:21
【问题描述】:

我正在尝试开发一个类结构。我在尝试访问父类的属性时遇到了问题(即当我继承BaseX和BaseY时,BaseX的属性出现在子类中而不是BaseY中)试图继承父类并在这里经历了不同的帖子,最后来到了下面的结构这类似于我对实际代码所做的事情;尽管只是为了理解概念,但我对下面的结构进行了更多的实验。实际代码从单个父类开始,然后多个第 1 层子类继承它,然后还有更多第 2 层子类,其数量少于第 1 层子类的数量,然后我将所有第 2 层子类合并为一个班级。现在经过大量研发,我发现下面的代码正在做我想做的事情(可以访问 SubABC 类中的所有属性)但我不确定这是否正确,因为尽管实现了这一点,但我不清楚这个超级() 在这里工作。在继续之前,我想清楚我在做什么。例如,为什么在 MidA 和 MidB 中单个 super() 就足够了,但在 MidC 中需要三个 super()。以及为什么 SubABC 只需要一个 super()。在将其复制到我的代码中之前,我将不胜感激。我不是 Python 专家,所以如果我需要阅读一些东西,请给我指明正确的方向 - 即关于 super() 的文档。此外,PyCharm(2019.3.1 - 社区版)抱怨 MidA super().init(x_var, z_var) 和 MidB super().init(x_var, z_var)声明说“意外的论点”;不确定这是否只是一个错误。谢谢。

class BaseX(object):
    def __init__(self, x_var):
        print('++++ Class BaseX')
        self.x_var = x_var
        print('---- Class BaseX')


class BaseY(object):
    def __init__(self):
        print('++++ Class BaseY')
        self.y_var = 'Y_Var'
        print('---- Class BaseY')


class BaseZ(object):
    def __init__(self, z_var):
        print('++++ Class BaseZ')
        self.z_var = z_var
        print('---- Class BaseZ')


class MidA(BaseX, BaseY, BaseZ):
    def __init__(self, x_var, z_var):
        print('++++ Class MidA')
        super().__init__(x_var, z_var)
        print('---- Class MidA')

    def mid_a_func(self):
        print(self.x_var)
        print(self.y_var)
        print(self.z_var)


class MidB(BaseX, BaseY, BaseZ):
    def __init__(self, x_var, z_var):
        print('++++ Class MidB')
        super().__init__(x_var, z_var)
        print('---- Class MidB')

    def mid_b_func(self):
        print(self.x_var)
        print(self.y_var)
        print(self.z_var)


class MidC(BaseX, BaseY, BaseZ):
    def __init__(self, x_var, z_var):
        print('++++ Class MidC')
        super().__init__(x_var)
        super(BaseX, self).__init__()
        super(BaseY, self).__init__(z_var)
        print('---- Class MidC')

    def mid_b_func(self):
        print(self.x_var)
        print(self.y_var)
        print(self.z_var)


class SubABC(MidA, MidB, MidC):
    def __init__(self, x_var, z_var):
        print('++++ Class SubABC')
        super().__init__(x_var, z_var)
        print('---- Class SubABC')

    def sub_ab_func(self):
        self.mid_a_func()

ab = SubABC('X_Var', 'Z_Var')

ab.sub_ab_func()

输出是:

++++ Class SubABC
++++ Class MidA
++++ Class MidB
++++ Class MidC
++++ Class BaseX
---- Class BaseX
++++ Class BaseY
---- Class BaseY
++++ Class BaseZ
---- Class BaseZ
---- Class MidC
---- Class MidB
---- Class MidA
---- Class SubABC
X_Var
Y_Var
Z_Var

【问题讨论】:

标签: python python-3.x


【解决方案1】:

出现这种意外行为的原因是 Python 中的 super 在不带参数调用时会返回继承树中下一个类的实例。

考虑以下示例(参数lvl 指的是缩进级别,因此我们可以更清楚地看到层次结构):

class Base:
    def __init__(self, lvl):
        print(lvl, "+ Base")
        print(lvl, "- Base")

class Mid(Base):
    def __init__(self, lvl):
        print(lvl, "+ Mid")
        super().__init__(lvl + "\t")
        print(lvl, "- Mid")

class Top(Mid):
    def __init__(self):
        print(" + Top")
        super().__init__("\t")
        print(" - Top")

t = Top()

让我们构建一个“继承树”:

- Top
    - Mid
        - Base

画成单线,可以这样表示:

Top -> Mid -> Base

所以,在Top 中,super() 返回下一项的实例,即Mid。然后在Mid中,返回一个Base的实例。

不出所料,我们得到了这个输出:

 + Top
     + Mid
         + Base
         - Base
     - Mid
 - Top

有趣的是,我们可以在不改变输出的情况下将代码更改为:

class Base:
    def __init__(self, lvl):
        print(lvl, "+ Base")
        print(lvl, "- Base")

class Mid:
    def __init__(self, lvl):
        print(lvl, "+ Mid")
        super().__init__(lvl + "\t")
        print(lvl, "- Mid")

class Top(Mid, Base):
    def __init__(self):
        print(" + Top")
        super().__init__("\t")
        print(" - Top")

t = Top()

因为这棵树是

- Top
    - Mid
    - Base

还是变成了

Top -> Mid -> Base

现在让我们看看您的示例中的“树”:


- SubABC
    - MidA
        - BaseX
        - BaseY
        - BaseZ
    - MidB
        - BaseX
        - BaseY
        - BaseZ
    - MidC
        - BaseX
        - BaseY
        - BaseZ

这就变成了

SubABC -> MidA -> MidB -> MidC -> BaseX -> BaseY -> BaseZ

因此,SubABC 中的 super() 转到 MidAMidA 中的 MidB 等等。

因此,当我们到达BaseX 时,我们会到达尽可能多的地方。 BaseX 没有对 super() 的调用,因此永远不会从它执行 BaseY。因此,我们必须显式调用BaseYBaseZ

如果我们将缩进级别的东西应用到您的代码中,我们会得到以下输出:


    ++++ Class SubABC
         ++++ Class MidA
             ++++ Class MidB
                 ++++ Class MidC
                     ++++ Class BaseX
                     ---- Class BaseX
                     ++++ Class BaseX
                     ---- Class BaseX
                     ++++ Class BaseY
                     ---- Class BaseY
                     ++++ Class BaseZ
                     ---- Class BaseZ
                 ---- Class MidC
             ---- Class MidB
         ---- Class MidA
    ---- Class SubABC

您必须在MidC 中使用super(BaseX, self).__init__() 而不是super(BaseY, self).__init__() 的原因是因为您需要调用下一个父级(BaseY)的方法,而达到此目的的唯一方法是运行super(),就好像你在BaseX的方法中一样。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-25
    • 2015-04-30
    • 2012-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多