Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python super calling child methods

There are numerous questions on the usage of super() but none of them appears to answer my question.

When calling super().__init__() from a subclass, all method calls in the super-constructor are actually taken from the subclass. Consider the following class structure:

class A(object):
    def __init__(self):
        print("initializing A")
        self.a()
    def a(self):
        print("A.a()")

class B(A):
    def __init__(self):
        super().__init__()
        # add stuff for B
        self.bnum=3 # required by B.a()        
    def a(self):
        print("B.a(), bnum=%i"%self.bnum)

b=B()

which fails with

initializing A
Traceback (most recent call last):
  File "classmagic.py", line 17, in 
    b=B()
  File "classmagic.py", line 11, in __init__
    super().__init__()
  File "classmagic.py", line 5, in __init__
    self.a()
  File "classmagic.py", line 15, in a
    print("B.a(), bnum=%i"%self.bnum)
AttributeError: 'B' object has no attribute 'bnum'

Here I call the super constructor in B() to initialize some basic structure (some of which is executed as an own function a()). However, if I override the a() function as well, this implementation is used when calling A's constructor which fails because A knows nothing about B and may use different internal variables.

This may or may not be intuitive, but what do I have to do when I want all methods in A only to have access to the functions implemented there?

like image 770
thias Avatar asked Sep 18 '12 13:09

thias


2 Answers

If your code has to call specific private methods that cannot be overridden, use a name that starts with two underscores:

class A(object):
    def __init__(self):
        print("initializing A")
        self.__a()
    def __a(self):
        print("A.a()")

class B(A):
    def __init__(self):
        super().__init__()
        # add stuff for B
        self.bnum=3 # required by B.a()        
    def __a(self):
        print("B.__a(), bnum=%i"%self.bnum)

Python "mangles" such method names by adding in the class name (plus an underscore) to minimize the chances subclasses overwrite them with their own versions.

The PEP 8 Python Style Guide has this to say about private name mangling:

If your class is intended to be subclassed, and you have attributes that you do not want subclasses to use, consider naming them with double leading underscores and no trailing underscores. This invokes Python's name mangling algorithm, where the name of the class is mangled into the attribute name. This helps avoid attribute name collisions should subclasses inadvertently contain attributes with the same name.

Note 1: Note that only the simple class name is used in the mangled name, so if a subclass chooses both the same class name and attribute name, you can still get name collisions.

Note 2: Name mangling can make certain uses, such as debugging and __getattr__(), less convenient. However the name mangling algorithm is well documented and easy to perform manually.

Note 3: Not everyone likes name mangling. Try to balance the need to avoid accidental name clashes with potential use by advanced callers.

like image 116
Martijn Pieters Avatar answered Oct 19 '22 18:10

Martijn Pieters


Rather than calling self.a(), you'll need to use A.a(self). But this is not common to do for all methods on a particular class and you should really re-evaluate whether B should inherit from A.

like image 21
mgilson Avatar answered Oct 19 '22 19:10

mgilson