Privacy in Python
There is nothing called “Private” Variable or Method in Python. You heard it right! Python does not believe in conventional Private-Public philosophy. A variable or a method which cannot be accessed outside the Class, does not exist in Python. Python assumes a basic understanding among Python developers saying “We are all adults here!”
So, what to do now?
Even though there is no way to have a pure (as I call it) Private Variable or Method, there is a convention among Python developers. Any variable which has an Underscore (‘_’) leading its name e.g. _var, is supposed to be a private variable or method i.e. it’s an indicator that ‘Don’t touch this member, it is supposed to be non-public‘.
Is there no way to restrict access to a class member outside the class? The answer is Yes and No. Actually, it lies somewhere in middle. Because there is a way to make a class member “behave” like a Private member (a kind of an illusion that works!). It is called Name Mangling.
Name Mangling is a process where if a method has at least two underscores leading the name and at the most one underscore trailing the name, it is textually replaced with _ClassName before it, for example, __method() becomes _ClassName__method(). Please note that the Name Mangling is primarily done to avoid accidents of overriding the methods of parent classes by inherited classes. We will take a look at it in some time.
Take a look at the following code. There are two variables _var1 and two methods __printVar1
class MyClass(object): def __init__(self,*args): self.__var1 = args def __printVar1(self): print(self._var1) printVar1 = __printVar1
Now, try running the following codes with the MyClass. (You can use Visual Studio Code to debug Python code. It is free! Here’s my a tutorial which will help you set up Visual Studio Code to debug Python code)
myObj = MyClass(10) print(myObj.__var1)
You will get an error as AttributeError: ‘MyClass’ object has no attribute ‘__var1’
myObj = MyClass(10) myObj.__printVar1()
You will get an error as AttributeError: ‘MyClass’ object has no attribute ‘__printVar1’
So, technically speaking we have ‘restricted’ the developer from directly accessing this method. We have successfully made a class method ‘behave’ like a private method even though it is not private.
The way to access the method __printVar1() outside MyClass is to assign it to a different variable, like printVar1 = __printVar1 in the code mentioned above. The variable printVar1 will not be mangled because it doesn’t have ‘__’ before it. So now, instead of using myObj.__printVar1() we can use myObj.printVar1() as mentioned below.
myObj = MyClass(10) myObj.printVar1()
The output will be as follows,
Let’s go through some tutorials to understand How Name Mangling works and how to overcome it?
Tutorial 1: Underscores and Name Mangling
Take a look at the following class definition
class MyClass(object): def __init__(self,*args): self.__var1 = args def _method1(self): print('_method1') def __method2(self): print('__method2') def __method3_(self): print('__method3_') def __method4__(self): print('__method4__') def __var1(self): return self.__var1 var1 = __var1
Now, run/debug the following code. Try to change the Class definition to resolve errors without changing the original member definitions. Please mention in the comments if you have any questions.
myObj = MyClass(10) myObj._method1() myObj.__method2() myObj.__method3_() myObj.__method4__() myObj.__var1(15)
Tutorial 2: Name Mangling and Overriding of Methods
Now, let’s see the actual reason why Name Mangling was introduced. According to official Python documentation “Name mangling is helpful for letting subclasses override methods without breaking intraclass method calls”
Take a look at following code
class MyClass(object): def __init__(self,*args): self.method1() def method1(self): print('Parent method1') class MySubClass(MyClass): def method1(self,*args): print('Child method1') myObj = MySubClass() myObj.method1()
What do you think will be the output? Right! The output will be like following
>> Child method1 Child method1
So, it is evident that the definition of method1() of MySubClass has overridden method1() of MyClass. So, how can we make sure that this ‘override accident‘ doesn’t occur?
Now, take a look at the following code. Please run/debug this code, let me know what is the output and also check if we have avoided the override accident! (Can you post the output in comments ?)
class MyClass(object): def __init__(self,*args): self.__method1() def method1(self): print('Parent method1') __method1 = method1 class MySubClass(MyClass): def method1(self,*args): print('Child method1') myObj = MySubClass() myObj.method1()
Tutorial 3: Name Mangling and How to Access Mangled Members?
Like I mentioned before there is nothing Private in Python. But, name mangling changes the privileges of accessing mangled members of a class.
So, the obvious question is ‘How to access mangled members?’
As we have seen before, due name mangling python interprets a class member __method() as _ClassName__method(). So, the simplest way to access this mangled member is to call it as it is interpreted. See the following code and try to understand the code and the way I have accessed mangled members. Can you try running this code and put the output in the comments?
class MyClass(object): _MyClass__var = 10 def __init__(self,*args): self.__method1() def method1(self): print('Parent method1') __method1 = method1 def var(self): print(self.__var) class MySubClass(MyClass): def method1(self,*args): print('Child method1') def method2(self): self._MyClass__method1() myObj = MySubClass() myObj._MyClass__method1() myObj.method1() myObj.method2() myObj.var() MyClass()._MyClass__method1()
I hope these tutorials will help you understand how Name Mangling works! If you have queries or questions please post them in the comments. I will try to answer them. Also, please share the tutorial if you liked.