I’m trying to create classes and subclasses in python with "protected" attributes using name mangling (__). However, when I try to extend the functionality of classes further with different subclasses, I get errors. So when is the appropriate time to use name mangling when the expectation is that both the parent class and the subclasses have unique use cases?
Here is a simple toy example to illustrate the question. Let’s say the Parent is a realist, so they see the value as it is. The PositiveChild sees things in a better light, so they see the value as a positive. The NegativeChild is a pessimist, so they see the value as a negative. However, both PositiveChild and NegativeChild understand how their parent feels, so they have access to the Parent‘s function.
class Parent():
def __init__(self, value: int) -> None:
self.__value = value
def how_parent_feels(self) -> None:
print(self.__value)
class PositiveChild(Parent):
def __init__(self, value: int) -> None:
super().__init__(value)
def how_child_feels(self) -> None:
print(abs(self.__value))
class NegativeChild(Parent):
def __init__(self, value: int) -> None:
super().__init__(value)
def how_child_feels(self) -> None:
print(abs(self.__value) * -1)
if __name__ == "__main__":
# This is our function.
val = 10
# Here is our parent.
mom = Parent(val)
# How does she feel?
mom.how_parent_feels()
>>> 10
# Here is her kids.
kid1 = PositiveChild(val)
kid2 = NegativeChild(val)
# Do they know how the mom feels?
kid1.how_parent_feels()
>>> 10
kid2.how_parent_feels()
>>> 10
# But how do THEY feel?
kid1.how_child_feels()
>>> AttributeError: 'PositiveChild' object has no attribute '_PositiveChild__value'
kid2.how_child_feels()
>>> AttributeError: 'NegativeChild' object has no attribute '_NegativeChild__value'
I understand that the name mangling is making both PositiveChild and NegativeChild to have a _Parent__value attribute, and this is what the inherited how_parent_feels() method expects and so this works fine. But the respective how_child_feels() methods are expecting a _PositiveChild__value or _NegativeChild__value attribute. So what are your options for re-using name mangled inherited attributes within subclass specific methods? Is this a misuse of "protected" python attributes and should be avoided altogether?
>Solution :
The entire point of using double-underscore name-mangling is to prevent name collisions in subclasses. That’s it. So this is precisely the behavior that is intended, what you have demonstrated is the canonical use case for double-underscore name mangling!
Or to be a little more precise, it is to prevent accidental name collisions. The whole point is to allow subclasses to define their own self.__value variable without having to worry if the parent class is using a name like that! It is to reproduce this one feature of "private" in languages with access modifiers, that is, no accidental name collisions.
It is crucial to understand, though, that isn’t private though, since these variables are "publicly" accessible.
If you really wanted to use double-underscore name-mangling this way, the straightforward solution is to use the correct variable, which is _Parent__value
#in a subclass
class PositiveChild(Parent):
def __init__(self, value: int) -> None:
super().__init__(value)
def how_child_feels(self) -> None:
# see, all variables are always public in Python
# this only prevents *accidental* name collisions
# nothing is stopping you from using this variable
print(abs(self._Parent__value))
If you want to mark a variable as restricted to internal use, you would commonly just use a single leading underscore.
Both of these approaches are equally as "private". Python doesn’t have private!
Note, in languages with access modifiers, like Java, if you wanted a variable that wasn’t exposed publicly, but was exposed to subclasses, you would use protected, so sometimes that terminology is used to refer to this idea in Python.