我的多类继承的实现是否正确?

发布于 2025-01-16 07:38:25 字数 2370 浏览 3 评论 0原文

我有一个关于 python 中的多类继承的问题。我认为我已经正确实现了它,但是它在某种程度上不符合我通常对继承的理解(特别是 super() 的使用),并且我不确定这是否会导致错误或某些属性不被更新等。

所以,让我尝试清楚地描述基本问题:

  • 我有三个类 Base,First 和 Second
  • First 和 Second 都需要从 Base 继承
  • Second 也从 First 继承
  • Base 是一个外部模块,它具有所需的某些基本方法First 和 Second 能够正确运行
  • First 是 Second 的基类,其中包含我必须在 Second
  • Second 中重复写下的方法是我使用的实际类。它实现了附加方法和属性。 Second 是一个设计可能有很大差异的类,因此我想灵活地更改它,而无需将所有代码都写在 Second 中。
  • 然而,关于 Second 最重要的一点是:如下所示,在 Second 的 init 中,我首先要从 Base 继承并执行一些需要 Base 方法的操作。然后,在此之后,我想在 First 的 init 中启动操作,这些操作操作在 Second 中实例化的一些参数。为此,我在 Second 的 init-body 末尾继承了 First。
  • 您可以看到在 Second 的整个初始化过程中如何操作变量 a。当前的行为如我所愿,但我的代码结构看起来有些奇怪,这就是我问的原因。
  • 我到底为什么要这么做?想象一下 First 类有许多方法,并且还在其 init 主体中执行许多操作(对 Second 中的参数)。我不想将所有这些方法放在 Second 的主体中,并将​​所有这些操作放在 Second 的 init 中(这里是参数 a)。首先是一个很少改变的类,因此为了清晰和紧凑,最好将其移回另一个文件,至少在我看来^^。另外,由于Second的init中的调用顺序,我没有找到其他方法来实现。

现在的代码:

class Base():
    
    def __init__(self):
        pass
    
    def base_method1(self):
        print('Base Method 1')
        pass
    
    def base_method2(self):
        pass
    
    # ...

class First(Base): 
    
    def __init__(self): 
        super().__init__()
        print('Add in Init')
        self.first_method1()
        
    def first_method1(self):
        self.a += 1.5
    
    def first_method2(self):
        pass
    
    # ...
        
class Second(First):
    
    def __init__(self,a):
        
        # set parameters
        self.a = a
        
        # inherit from Base class
        Base.__init__(self)
        
        # some operations that rely on Base-methods
        self.base_method1()
        
        print(self.a)
        
        # inherit from First and perform operations in First-init
        # that must follow AFTER the body of Second-init
        First.__init__(self)
        
        print(self.a)
    
        # checking whether Second has inherited the method(s) from First
        print('Add by calling method')
        self.first_method1()
        
        print(self.a)
        
sec = Second(0)

语句 sec = Second(0) 的输出打印:

Base Method 1
0
Add in Init
1.5
Add by calling method
3.0

我希望它或多或少清晰;如果没有,我很高兴澄清! 谢谢,我很感激任何评论!

最好的,JZ

I have a question about multiple class inheritance in python. I think that I have already implemented it correctly, however it is somehow not in line with my usual understanding of inheritance (the usage of super(), specifically) and I am not really sure whether this could lead to errors or certain attributes not be updated etc.

So, let me try to describe the basic problem clearly:

  • I have three classes Base, First and Second
  • Both First and Second need to inherit from Base
  • Second also inherits from First
  • Base is an external module that has certain base methods needed for First and Second to function correctly
  • First is a base class for Second, which contains methods that I would have to repetitively write down in Second
  • Second is the actual class that I use. It implements additional methods and attributes. Second is a class for which the design may vary a lot, so I want to flexibly change it without having all the code from first written in Second.
  • The most important point about Second however is the following: As visible below, in Second's init, I firstly want to inherit from Base and perform some operations that require methods from Base. Then, after that, I would like to launch the operations in the init of First, which manipulate some of the parameters that are instantiated in Second. For that, I inherit from First at the end of Second's init-body.
  • You can see how the variable a is manipulated by throughout the initialization of Second. The current behavior is as I wish, but the structure of my code looks somehow weird, which is why I am asking.
  • Why the hell do I want to do this? Think of the First class having many methods and also performing many operations (on parameters from Second) in it's init body. I don't want to have all these methods in the body of Second and all these operations in the init of Second (here the parameter a). First is a class that will rarely change, so it is better for clarity and compactness to move it back to another file, at least in my opinion ^^. Also, due to the sequence of calls in Second's init, I did not find another way to realize it.

Now the code:

class Base():
    
    def __init__(self):
        pass
    
    def base_method1(self):
        print('Base Method 1')
        pass
    
    def base_method2(self):
        pass
    
    # ...

class First(Base): 
    
    def __init__(self): 
        super().__init__()
        print('Add in Init')
        self.first_method1()
        
    def first_method1(self):
        self.a += 1.5
    
    def first_method2(self):
        pass
    
    # ...
        
class Second(First):
    
    def __init__(self,a):
        
        # set parameters
        self.a = a
        
        # inherit from Base class
        Base.__init__(self)
        
        # some operations that rely on Base-methods
        self.base_method1()
        
        print(self.a)
        
        # inherit from First and perform operations in First-init
        # that must follow AFTER the body of Second-init
        First.__init__(self)
        
        print(self.a)
    
        # checking whether Second has inherited the method(s) from First
        print('Add by calling method')
        self.first_method1()
        
        print(self.a)
        
sec = Second(0)

The output of the statement sec = Second(0) prints:

Base Method 1
0
Add in Init
1.5
Add by calling method
3.0

I hope it is more or less clear; if not, I am glad to clarify!
Thanks, I appreciate any comment!

Best, JZ

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

风吹短裙飘 2025-01-23 07:38:25

所以 - 这里的基本问题是你正在尝试一些多重继承本身并不是解决方案的东西 - 但是有一些方法可以构建你的代码以便它们工作。

在 Python 中使用适当的多重继承时,只需在基类之外的每个已定义方法中调用 super().method 一次,并且仅调用一次,并且不要像在 Second 类上的 Base.__init__() 中那样通过硬编码祖先来调用该方法的特定版本。首先,按原样进行设计,每次实例化 Second 时,Base.__init__() 都会运行两次。

您的假设的主要问题在于

我首先想继承Base并执行一些需要Base方法的操作。然后,在此之后,我想在 First 的 init 中启动操作,这些操作操作在 Second 中实例化的一些参数。为此,我在 Second 的 init-body 末尾继承了 First。

因此,如果您如您所见,调用 super(),无论您是否将 Second 写为 class Second(Base, First) , super 将在 Base 中的方法之前运行 First 中的方法。发生这种情况是因为当存在多重继承时,Python 确实会线性化所有祖先类,因此始终存在可预测且确定性的顺序,其中每个祖先类仅出现一次,以查找属性并调用方法(“mro”或“方法解析顺序”) - 唯一可能的安排方式是进入第二 -> 第一 -> 基地。

现在,如果您确实想在 First 中运行初始化之前在 Base 中执行某些部分的初始化,这就是“不仅仅是多重继承”发挥作用的地方:您有在你的类中定义 - 这是你自己做的设计决定,并且语言上没有任何现成的东西:就 Base.__init__ 的一些方法约定达成一致将在初始化的不同状态下调用,以便它们在适当的时间。

这样,您甚至可以跳过 First.__init__ 方法 - 只需使用您知道将由 Base.__init__ 调用子级的一些方法,然后根据需要重写这些方法。

当语言本身同时提供 __new__ 和 __init__ 方法规范时,它会使用此策略:每个方法规范都将运行新实例初始化的不同阶段。

请注意,通过这样做,在这种情况下您甚至不需要多重继承:

class Base():
    
    def __init__(self):
        # initial step
        self.base_method1()
        ...
        # first initalization step:
        self.init_1()
        #Intemediate common initialization
        ...
        # second initialization step
        ...

    def init_1(self):
        pass

    def init_2(self):
        pass

    def base_method1(self):
        pass

    # ...

class First(Base): 
    
    # __init__ actually uneeded here:
    #def __init__(self): 
    #    super().__init__()
    def init_1(self):
        print('Add in Init')
        self.first_method1()
        return super().init_1()
        
    # init_2 not used,
    def first_method1(self):
        self.a += 1.5
    
    def first_method2(self):
        pass
    
    # ...
        
class Second(First):
    
    def __init__(self,a):
        
        # set parameters
        self.a = a
        
        # some operations that rely on Base-methods
        super().__init__()  # will call Base.__init__ which should call "base_method1"
        
        # if you need access to self.a _before_  it is changed by First, you implement
        # the "init_1" slot  in this  "Second" class
            
        # At this point, the code in First that updates the attribute has already run
        
        print(self.a)


    def init_1(self):
        # this part of the code will be called by Base.__init__ 
        _before_  First.init_1 is executed:
        print("Initial value of self.a", a)
        # delegate to "init_1" stage on First:
        return super().init_1()
        
sec = Second(0)

So - the basic problem here is that you are trying something for which multiple-inheritance is not a solution on itself - however there are ways to structure your code so that they work.

When using multiple-inheritance proper in Python, one should only have to call super().method once in each defined method, out of the base class, and only once, and do not call specific versions of the method by hardcoding an ancestor like you do in Base.__init__() on Second class. Just for start, with this design as is, Base.__init__() will run twice each time you instantiate Second.

The main problem in your assumptions lies in

I firstly want to inherit from Base and perform some operations that require methods from Base. Then, after that, I would like to launch the operations in the init of First, which manipulate some of the parameters that are instantiated in Second. For that, I inherit from First at the end of Second's init-body.

So - if you call super()as you shall have perceived, no matter if you write Second as class Second(Base, First), super will run the method in First before the method in Base. It happens because Python does linearize all ancestor classes when there is multiple-inheritance, so that there is always a predictable and deterministic order, in which each ancestor class shows up only once, to find attributes and call methods (the "mro" or "Method Resolution Order") - and the only possible way with your arrangement is to go Second->First->Base.

Now, if you really want to perform initialization of certain parts in Base prior to running intialization in First, that is where "more than multiple inheritance" comes into play: you have to define slots in your class - that is a design decision you do by yourself, and nothing ready-made on the language: agree on some convention of methods that Base.__init__ will call at varying states of the initialization, so that they are activated at the proper time.

This way you could even skip having a First.__init__ method - just have some method you know will be called on the child, by Base.__init__ and override those as you need.

The language itself use this strategy when it offers both a __new__ and a __init__ method spec: each will run a different stage of initialization of a new instance.

Note that by doing it this way, you don't even need multiple inheritance for this case:

class Base():
    
    def __init__(self):
        # initial step
        self.base_method1()
        ...
        # first initalization step:
        self.init_1()
        #Intemediate common initialization
        ...
        # second initialization step
        ...

    def init_1(self):
        pass

    def init_2(self):
        pass

    def base_method1(self):
        pass

    # ...

class First(Base): 
    
    # __init__ actually uneeded here:
    #def __init__(self): 
    #    super().__init__()
    def init_1(self):
        print('Add in Init')
        self.first_method1()
        return super().init_1()
        
    # init_2 not used,
    def first_method1(self):
        self.a += 1.5
    
    def first_method2(self):
        pass
    
    # ...
        
class Second(First):
    
    def __init__(self,a):
        
        # set parameters
        self.a = a
        
        # some operations that rely on Base-methods
        super().__init__()  # will call Base.__init__ which should call "base_method1"
        
        # if you need access to self.a _before_  it is changed by First, you implement
        # the "init_1" slot  in this  "Second" class
            
        # At this point, the code in First that updates the attribute has already run
        
        print(self.a)


    def init_1(self):
        # this part of the code will be called by Base.__init__ 
        _before_  First.init_1 is executed:
        print("Initial value of self.a", a)
        # delegate to "init_1" stage on First:
        return super().init_1()
        
sec = Second(0)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文