Reputation: 11243
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
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
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