Alois Mahdal
Alois Mahdal

Reputation: 11243

Child method is run instead of parent's?

I have following class inheritance schema:

object <- A <- B <- Z

Now each class will have public setup() method, which calls other "private" method _configure(), which contains loading code that I don't want to clutter up setup(). The point is to make every class "know" how to set itself up, and do it before other executions.

So what I want when calling setup on Z's instance is to run both A's setup and B's setup (order is not particularly important now), while each setup using _configure as defined in its own class.

Now following script

#!/usr/bin/python

class A(object):

    def __init__(self):
        self.configured = []
        self.set_up = []

    def _configure(self):
        self.configured.append("A")

    def setup(self):
        self._configure()       # calls B._configure()!
        self.set_up.append("A")

class B(A):

    def _configure(self):
        self.configured.append("B")

    def setup(self):
        super(B, self).setup()
        self._configure()
        self.set_up.append("B")

class Z(B):
    pass

if __name__ == "__main__":
    z = Z()
    z.setup()
    print "configured: %s" % z.configured
    print "set up: %s" % z.set_up

runs B._configure() twice, therefore returns

me@here:~$ ./t.py 
configured: ['B', 'B']
set up: ['A', 'B']
me@here:~$

instead of configured: ['A', 'B']....

Could somebody explain this to me? How should I make sure that A.setup calls A._configure?

Workaround: What worked for now was replacing self._configure with A._configure(self), but that seems ugly and non-OOP: now each class that could be potentially inherited should repeat its name with every method call? Where is the beauty and brevity of self?

Upvotes: 1

Views: 206

Answers (2)

Alois Mahdal
Alois Mahdal

Reputation: 11243

Finally I got it, the necessary magic is called name mangling or name scrambling.

The solution is as simple as using two underscores in the name of the configuring method. When Python sees such funny named method (or other attribute), it will silently rename it to _A__configure, so that proper method is called even if user (accidentally or not) overwrote it.

(Until now, I thought that "two underscores" are reserved for Python internals, humbly avoiding them all the time...)

Fixed code:

#!/usr/bin/python

class A(object):

    def __init__(self):
        self.configured = []
        self.set_up = []

    def __configure(self):
        self.configured.append("A")

    def setup(self):
        self.__configure()
        self.set_up.append("A")

class B(A):

    def __configure(self):
        self.configured.append("B")

    def setup(self):
        super(B, self).setup()
        self.__configure()
        self.set_up.append("B")

class Z(B):
    pass

if __name__ == "__main__":
    z = Z()
    z.setup()
    print "configured: %s" % z.configured
    print "set up: %s" % z.set_up

returns the desired

me@here:~$ ./t.py 
configured: ['A', 'B']
set up: ['A', 'B']
me@here:~$

Upvotes: 1

mrcasals
mrcasals

Reputation: 1171

The setup method in class B calls super, so it calls the setup method from A... This is the order of execution of your methods:

Z.setup()
  B.setup()
    A.setup (via super)
      B._configure
        B.configured.append("B")
      B.setup.append("A")
    B._configure
      B.configured.append("B")
  B.set_up.append("B")

This means that Z.setup calls B.setup via inheritance, which calls A.setup via super, which calls B._configure. B.setup calls again B._configure and return after the append.

Hope this makes it clear (more or less)

UPDATE: Here's a little explanation of what's happening: You are defining a class Z which has a public method setup and a private method _configure. Those methods are not defined in Z class but on its parent, class B. But you can still call Z.setup and Z._configure thanks to inheritance.

Now, the Z.setup method uses the definition in B class in order to work. This calls super in order to call the code defined in A.setup while still being Z, so when the method _configure is called, which definition will be used? As we are Z (that is, self is an instance of Z), the method that will be called will be Z._configure, because that's what inheritence does.

Look at the code again: you are overwriting in B the _configure method first defined in A. As there's no call to super in B._configure, A._configure will never be called if the object is an instance of a class that inherits from A.

TL;DR: You can't call A._configure from Z if B defines _configure without calling super. That's inheritance. If you want to call A.configure, either don't overwrite the method in B or use super.

Hope this helps! :)

Upvotes: 3

Related Questions