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!
>>>Return to Python Tutorial Page
Related Topics
You may also like-