How does a method of a subclass become a method of the base class?

Cameron Simpson cs at cskk.id.au
Sun Mar 26 21:03:29 EDT 2023


On 27Mar2023 01:53, Jen Kris <jenkris at tutanota.com> wrote:
>Thanks for your reply.  You are correct about the class definition 
>lines – e.g. class EqualityConstraint(BinaryConstraint).  I didn’t post 
>all of the code because this program is over 600 lines long.  It's 
>DeltaBlue in the Python benchmark suite. 

Doubtless. But the "class ...:" line is the critical thing.

>I’ve done some more work since this morning, and now I see what’s happening.  But it gave rise to another question, which I’ll ask at the end. 
>
>The call chain starts at
>
>    EqualityConstraint(prev, v, Strength.REQUIRED) 
>
>The class EqualityConstraint is a subclass of BinaryConstraint.  The entire class code is:
>
>    class EqualityConstraint(BinaryConstraint):
>        def execute(self):
>            self.output().value = self.input().value

Ok, so it is effectively a BinaryConstraint with a single customised 
execute() method.

>Because EqualityConstraint is a subclass of BinaryConstraint, the init method of BinaryConstraint is called first.

That is courtesy of the MRO: EqualityConstraint has no __init__, so the 
one found in BinaryConstraint is found and used.

>During that initialization (I showed the call chain in my previous 
>message), it calls choose_method.  When I inspect the code at 
>"self.choose_method(mark):" in PyCharm, it shows:
>
>    <bound method BinaryConstraint.choose_method of <Trans_01_DeltaBlue.EqualityConstraint object at 0x7f7ac09c6ee0>>

This says that it found found the choose_method method in the 
BinaryConstraint class definition, but that it is _bound_ to the 
EqualityConstraint instance you're using (self).

>As EqualityConstraint is a subclass of BinaryConstraint it has bound the choose method from BinaryConstraint, apparently during the BinaryConstraint init process, and that’s the one it uses.  So that answers my original question. 

Incorrectly, I think.

__init__ does _nothing_ about other methods, if you mean "__init__" in 
the sentence above.

The class definition results in an EqualityConstraint.__mro__ list of 
classes, which is where _every_ method is looked for _at call time_.

>But that brings up a new question.  I can create a class instance with 
>x = BinaryConstraint(),

That makes an instance of EqualityConstraint.

>but what happens when I have a line like "EqualityConstraint(prev, v, 
>Strength.REQUIRED)"?

That makes an instance of EqualityConstraint.

>Is it because the only method of EqualityConstraint is execute(self)?

I don't know what you're asking here.

>Is execute a special function like a class __init__?

No. It's just a method.

In a sense, __init__ isn't very special either. It is just _called_ 
during new instance setup as the final step. By that time, self is a 
fully set up instance of... whatever and the __init__ function is just 
being called to do whatever initial attribute setup every instance of 
that class needs.

Python doesn't magicly call _all_ the __init__ methods of all the 
superclasses; it calls the first found. So for an EqualityConstraint it 
finds the one from BinaryConstraint because EqualityConstraint does not 
provide an overriding one of its own.

If EqualityConstraint _did_ provide one, it might typically be 
structured as:

     class EqualityConstraint(BinaryConstraint):
         def __init__(self, prev, v, strength, something_extra):
             super().__init__(prev, v, strength)
             self.extra = something_extra

So:
- Python only calls _one_ __init__ method
- in the example above, it finds one in EqualityConstraint and uses it
- because Python calls _only_ that, in order to _also_ do the normal 
   setup a BinaryConstraint needs, we pass the parameters used by 
   BinaryConstraint to the new __init__ up the chain by calling 
   super().__init__(BinaryConstraint-parameters-here)
- we do our own special something with the something_extra

That super().__init__() call is the _only_ thing which arranges that 
superclass inits get to run. This givens you full control to sompletely 
replace some superclass' init with a custom one. By calling 
super().__init__() we're saying we not replacing that stuff, we're 
running the old stuff and just doing something additional for our 
subclass.

Cheers,
Cameron Simpson <cs at cskk.id.au>


More information about the Python-list mailing list