Search code examples
pythoninheritancename-mangling

Attribute name mangled with parent class name (at first parsing) instead of child class name (at instantiation/call)


I ran into some unexpected behaviour when trying to test some code, where I was trying to access the name-mangled attribute of a child class (__var). Instead of getting the child's value, I thought I was getting the parent class's __var instead.

Final Edit: The child object accesses it's own version of the variable, name-mangled with the parent's name instead of its own. The previously unexpected behaviour is now understood.

class test_parent(object):
    __var = 1

    def getvar(self):
        a = self.__class__.__var
        return a

class test_child(test_parent):
    __var = 2

a = test_parent()
b = test_child()
b.__class__._test_parent__var = 3

print(a.getvar())                       # Prints 1, as expected
print(b.__class__._test_child__var)     # Prints 2, as expected
print(b.__class__._test_parent__var)    # Prints 3, as expected
print(b.getvar())                       # Prints 3

Solution

  • Answered with help from the comments:

    Python's name-mangling occurs when the method is 'read' by the interpreter (probably not the correct terminology), not when it is called. The tests evidence the process that happens in the background.

    When __var is first encountered, it is inside the body of test_parent and is name-mangled:

    class test_parent(object):
        __var = 1
    

    becomes:

    class test_parent(object):
        _test_parent__var = 1
    

    A similar thing happens when __var is encountered in test_child, becoming _test_child__var. The previously unexpected behaviour came about because the same thing happened inside getvar.

    class test_parent(object):
        ...
        def getvar(self):
            ...
            a = self.__class__.__var
    

    becomes:

    class test_parent(object):
        ...
        def getvar(self):
            ...
            a = self.__class__._test__parent_var
    

    That is why the test code b.getvar() returns 3 once b.__class__._test_parent__var is assigned to it, because that is the value accessed by the getvar method.