In Python there are no explicit access modifiers so you can’t mark a class member as public/private. Then the question is how to restrict access to a variable or method outside the class, if required. Class member can be made private (Close to private actually) using a process called name mangling in Python.
Name mangling (Making member private)
In Python name mangling process any identifier with at least two leading underscores, at most one trailing underscore is textually replaced with _classname__identifier
where classname is the current class name. For example if there is a variable __var it is rewritten by the Python interpreter in the form _classname__var.
Since the name of any such class member (with at least two leading underscores, at most one trailing underscore) changes internally thus it can’t be accessed
using the given name. That is the closest Python goes for making a class member private.
Python Name mangling example
Let’s try to clarify name mangling process with examples.
class Person:
def __init__(self, name, age=0):
self.name = name
self.__age = age
def display(self):
print(self.name)
print(self.__age)
person = Person('John', 40)
#accessing using class method
print('Displaying values using class method')
person.display()
#accessing directly from outside
print('Trying to access variables from outside the class ')
print(person.name)
print(person.__age)
Output
Displaying values using class method
John
40
Traceback (most recent call last):
File "F:/NETJS/NetJS_2017/Python/Test/Person.py", line 21, in <module>
Trying to access variables from outside the class
John
print(person.__age)
AttributeError: 'Person' object has no attribute '__age'
As you can see variable __age (having two leading underscores) is not accessible from outside the class. Using a method with in the class it can still be accessed.
Same way for a method with two leading underscores.
class Person:
def __init__(self, name, age=0):
self.name = name
self.__age = age
def __displayAge(self):
print(self.name)
print(self.__age)
person = Person('John', 40)
person.__displayAge()
Output
Traceback (most recent call last):
File "F:/NETJS/NetJS_2017/Python/Test/Person.py", line 15, in <module>
person.__displayAge()
AttributeError: 'Person' object has no attribute '__displayAge'
As you can see method is not accessible from outside the class.
How does name change in Name mangling
If you want to verify the rewriting of name in Python name mangling process you can do so using the dir() function.
When a class object is passed as an argument to dir() function, it returns a list of valid attributes for that object.
class Person:
def __init__(self, name, age=0):
self.name = name
self.__age = age
person = Person('John', 40)
print(dir(person))
Output
['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__',
'__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__', 'name']
From the output of dir() function for Person object you can see that the __age is rewritten as _Person__age.
Name mangling and method overriding
As per Python docs stated objective of name mangling is to avoid name clashes of names with names defined by subclasses. Name mangling is helpful for letting
subclasses override methods without breaking intraclass method calls.
For example consider the following scenario where Parent class is subclassed and there is an overridden method test is the Child class too. From the constructor
of Parent class there is a call to test method- self.test()
class Parent:
def __init__(self):
print('in init')
self.test()
def test(self):
print('In Parent test method')
class Child(Parent):
def test(self):
print('In Child test method')
obj = Child()
obj.test()
Output
in init
In Child test method
In Child test method
As you can see Child test method is getting called both of the times. To avoid that name clash you can create a private copy of the original method.
class Parent:
def __init__(self):
print('in init')
self.__test()
def test(self):
print('In Parent test method')
# private copy
__test = test
class Child(Parent):
def test(self):
print('In Child test method')
obj = Child()
obj.test()
Output
in init
In Parent test method
In Child test method
Accessing name mangled class members
As already stated Python name mangling process rewrites the member name by adding _classname to the member. Thus it is still possible to access the class
member from outside the class by using the rewritten name. That is why it is said that Name mangling is the closest to private not exactly private.
class Person:
def __init__(self, name, age=0):
self.name = name
self.__age = age
def display(self):
print(self.name)
print(self.__age)
person = Person('John', 40)
print('Trying to access variables from outside the class ')
print(person.name)
print(person._Person__age)
Output
Trying to access variables from outside the class
John
40
As you can see private class member is accessed from outside the class by using the name mangled form _ClassName__var.
That's all for this topic Name Mangling in Python. If you have any doubt or any suggestions to make please drop a comment. Thanks!
Related Topics
-
Python Installation on Windows
-
Encapsulation in Python
-
Method Overriding in Python
-
Multiple Inheritance in Python
-
Python for Loop With Examples
You may also like-
-
Passing Object of The Class as Parameter in Python
-
Local, Nonlocal And Global Variables in Python
-
Python count() method - Counting Substrings
-
Python Functions : Returning Multiple Values
-
Marker Interface in Java
-
Functional Interfaces in Java
-
Difference Between Checked And Unchecked Exceptions in Java
-
Race Condition in Java Multi-Threading