Access Modifiers and Name Mangling in Python
In this article, you will learn everything you need to know about Python Access Modifiers/Specifiers. Regardless of the language this concept plays a vital role in programming.
As the name implies, “Access Modifiers” specify, what kind of access level for a particular attribute or method should have in a class. Basically, most languages have 3 levels of access modifiers.
- Public
- Protected
- Private
And also most languages have keywords for the particular access modifier to strictly define the access level. But python doesn’t have a strict way or any keyword for this. Python only has a convention for this. So how do we use access modifier in python? Let’s see one by one.
Public Access Modifier
This has nothing special and can be accessed from anywhere in the program. If you define an attribute without one or more leading _ (underscore), that would be public by default.
Protected Access Modifier
If you define an attribute or method name with a single leading underscore, that is considered as a protected member of the class.
Protected members are only meant to be accessed within the class and also the child classes which inherited from it. See the example below
class Parent:
_status = "I am protected" # Protected attribute
def _get_status(self): # Protected method
# Accessing protected attribute within the class is OK.
return self._status
def print_status(self): # Public method
# Accessing protected attribute within the class is OK.
print(self._status)
class Child(Parent):
def print_name(self): # Public method
# Accessing protected method from child class is OK.
print(self._get_status())
parent = Parent()
child = Child()
# Accessing protected attribute _status within the class (Recommended).
parent.print_status()
# Accessing protected method _get_status outside the class (Not Recommended).
print(parent._get_status())
# Accessing protected attribute _status outside the class (Not Recommended).
print(Parent._status)
In fact, these protected members in python are just a convention. No error will be raised, if you try to access protected members from outside the class. But as a good practice, you should only access protected members within the class or from the child classes which inherited from it.
Private Access Modifier
In Python, private members are defined with 2 leading underscores and should not have 2 or more trailing underscores.
Private members are only meant to be accessed within the class. They should not be accessed outside the class or even from child classes. Let’s see an example.
class Parent:
__status = "I am private" # private attribute
def __get_status(self): # Private method
# Accessing private attribute within the class is OK.
return self.__status
def print_status(self): # Public method
# Accessing private attribute within the class is OK.
print(self.__status)
class Child(Parent):
def print_name(self): # Public method
# Accessing private method from child will generate an error.
print(self.__get_status())
parent = Parent()
child = Child()
# Accessing private attribute __status within the class is ok.
parent.print_status()
# Accessing private method __get_status outside the class,
# will generate an error
print(parent.__get_status())
When we try to access protected members from outside the class, that didn’t generate an error. But in above example, when we try to access a private member in the last code line, that will generate an AttributeError. Why is that? Are private members actually private in Python?
Of course they are not. Even if python generate an error when accessing private members from outside the class, they are not yet secured. The reason we are getting an error is, Python perform an operation on private members called Name Mangling. Let’s see what it does.
Name Mangling in Python
When private members are defined, Python convers it’s name into following format.
_<ClassName><Attribute or method name>
Let’s see an example.
class MyClass:
__status = "I am private" # private attribute
def __get_status(self): # private method
print(self.__status)
Now if we look at the namespace of the above class with dir(MyClass), it will look something like this.
[‘_MyClass__get_status’, ‘_MyClass__status’, ‘__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__’]
As you can see, __get_status_ has changed into, _Parent__get__status_ and __status has changed into _Parent__status. So, Python didn’t actually make things private but changed the namespace into another format. This is called as “Name Mangling”.
Now if we try to access these private members with those mangled names, we will not get an error.
cls = MyClass()
# Accessing private members with mangled names,
# will not generate an error.
cls._MyClass__get_status()
cls._MyClass__status
# Accessing private members with defined names,
# will generate errors
cls.__status
cls.__get_status()
But again, same as in the protected members, you should not access private members outside the defined class as a best practice.
So in Python, access modifiers are just some conventions. You are not strictly limited for accessing any of those access levels. But as a good practice, you should always follow these conventions to write better Python codes.
That’s the story about “Python Access Modifiers”. If you find this article helpful, give it a clap. Thank you for reading to the end.