ChatGPT解决这个技术问题 Extra ChatGPT

Chain-calling parent initialisers in python [duplicate]

This question already has answers here: How to invoke the super constructor in Python? (7 answers) Closed 5 years ago.

Consider this - a base class A, class B inheriting from A, class C inheriting from B. What is a generic way to call a parent class initialiser in an initialiser? If this still sounds too vague, here's some code.

class A(object):
    def __init__(self):
        print "Initialiser A was called"

class B(A):
    def __init__(self):
        super(B,self).__init__()
        print "Initialiser B was called"

class C(B):
    def __init__(self):
        super(C,self).__init__()
        print "Initialiser C was called"

c = C()

This is how I do it now. But it still seems a bit too non-generic - you still must pass a correct type by hand.

Now, I've tried using self.__class__ as a first argument to super(), but, obviously it doesn't work - if you put it in the initialiser for C - fair enough, B's initialiser gets called. If you do the same in B, "self" still points to an instance of C so you end up calling B's initialiser again (this ends in an infinite recursion).

There is no need to think about diamond inheritance for now, I am just interested in solving this specific problem.

"But it still seems a bit too non-generic - you still must pass a correct type by hand."?? You're passing the class name to super(). You never need to know the superclass or anything else. How is this non-generic? What problem does it create? Can you give an example where this breaks?
I am not ready to answer this question if full. But anyway, what if the class gets renamed? What if I want to write a decorator function to automate this kind of chaining? Now I see, that this is not possible, but at the time of asking the question I did not know that.
@shylent, if I understand your question, what your describing is possible. You can use super() to make parent references fully generic if you understand the mro. Google "python super is super", watch the video.
@shylent actually, reading your question again I believe the infinite recursion you're experiencing is not what you think it is. I believe the mro order is making you call a constructor you're not intending to. I just dealt with this same thing myself not long ago. No matter what you pass to the first argument of super, super will call the first constructor in the mro, not the type you passed in. For instance if you call super(self.__class__, self) in the constructor of C, you will still call B's constructor because it is the first parent in the mro.

i
ironfroggy

Python 3 includes an improved super() which allows use like this:

super().__init__(args)

if I have more than one parent class how will super identify the which parent class to invoke ?
@kracekumar It will be determined by the class' Method Resolution Order (MRO), which you can find out by calling MyClassName.mro(). I may be incorrect, but I believe that the first specified parent is the one whose __init__ will be called.
@ironfroggy, would you mind elaborating on How does this improved super help with the question? I just found this old thread and I am having the same exact questions. Also Have things improved for Python 2.x since this question was first asked?
@user815423426 Because it makes the up-calls more generic, no longer repeating the class name, which was the subject of the original question.
@kracekumar, super(X,obj) actually returns a special "super" object that, when you do an attribute access on it, will return the attribute that would have been returned had class X had no attributes at all. for example, super(X,obj).__init__ will return the init method of your obj as if init in class X not been there. Now, if class X has two parents, and X.__init__ doesnt exist, the default behavior is to call init of only one of the parents, not both. This is effectively what super is doing... giving you one of the parents.
d
dF.

The way you are doing it is indeed the recommended one (for Python 2.x).

The issue of whether the class is passed explicitly to super is a matter of style rather than functionality. Passing the class to super fits in with Python's philosophy of "explicit is better than implicit".


I've had a really hard time deciding which answer to accept, but I will accept this one, simply because it is relevant to the version of python I am using.
In Python 2.6 I get TypeError: super() argument 1 must be type, not classobj using this method. Am I missing something?
@Leopd: super() only works with new-style classes, i.e. you must inherit from object.
Python IS a strongly typed language, but it is dynamically typed. There is a difference between weak and dynamic typing.
@TheScienceBoy the definitions are clear though. Python is strong-dynamic. Something like Java is strong-static. PHP is weak-dynamic. Static typing guarantees (within some limits at least) that input parameters are the correct type. Strong typing means that the compiler/runtime will not attempt to coerce the value of one type to another.. like changing "1" to a 1 so you can do math on it. Please see here: wiki.python.org/moin/…
b
bignose

You can simply write :

class A(object):

    def __init__(self):
        print "Initialiser A was called"

class B(A):

    def __init__(self):
        A.__init__(self)
        # A.__init__(self,<parameters>) if you want to call with parameters
        print "Initialiser B was called"

class C(B):

    def __init__(self):
        # A.__init__(self) # if you want to call most super class...
        B.__init__(self)
        print "Initialiser C was called"