Access Modifiers in Python : Public, Private and Protected
Prerequisites: Underscore (_) in Python, Private Variables in Python
Encapsulation is one of the four principles used in Object Oriented Paradigm. It is used to bind and hide data to the class. Data hiding is also referred as Scoping and the accessibility of a method or a field of a class can be changed by the developer. The implementation of scoping is different for different programming language. For example, statically typed, compiled language has direct support to scoping with the help of keywords which are mentioned when the method or field is declared. However Python does not have such keywords since it is a scripting language, and it is interpreted instead of being compiled. Mainly, Access Modifiers can be categorized as Public, Protected and Private in a class.
Python uses the ‘_’ symbol to determine the access control for a specific data member or a member function of a class. Access specifiers in Python have an important role to play in securing data from unauthorized access and in preventing it from being exploited. But it is not like other languages like Java and C++ since Python uses the concept of Name Mangling for achieving data hiding.
A Class in Python has three types of access modifiers:
- Public Access Modifier: Theoretically, public methods and fields can be accessed directly by any class.
- Protected Access Modifier: Theoretically, protected methods and fields can be accessed within the same class it is declared and its subclass.
- Private Access Modifier: Theoretically, private methods and fields can be only accessed within the same class it is declared.
We are mentioning “Theoretically” because python doesn’t follow the textbook definition of such specifications. Instead, it depends on the programmer/organization as well as a unique feature of python called as name mangling using which we can mimic the actual security provided by access modifiers.
Public Access Modifier:
The members of a class that are declared public are easily accessible from any part of the program. All data members and member functions of a class are public by default.
# program to illustrate public access modifier in a class
class Geek:
# constructor
def __init__(self, name, age):
# public data members
self.geekName = name
self.geekAge = age
# public member function
def displayAge(self):
# accessing public data member
print("Age: ", self.geekAge)
# creating object of the class
obj = Geek("R2J", 20)
# finding all the fields and methods which are present inside obj
print("List of fields and methods inside obj:", dir(obj))
# accessing public data member
print("Name:", obj.geekName)
# calling public member function of the class
obj.displayAge()
Output
List of fields and methods inside obj: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'displayAge', 'geekAge', 'geekName']
Name: R2J
Age: 20
We are using dir() function to list down all the member variables and functions of the Geeks object which can be accessed. We can clearly see geekName, geekAge, displayAge and other inbuilt methods such as __str__, __sizeof__, etc. In the above program, geekName and geekAge are supposed to be public data members and displayAge() method is a public member function of the class Geek. These data members of the class Geek can be accessed from anywhere in the program since they are present in the list returned by dir() as it is.
Protected Access Modifier:
The members of a class that are declared protected are only accessible within the class where it is declared and its subclass. To implement protected field or method, the developer follows a specific convention mostly by adding prefix to the variable or function name. Popularly, a single underscore “_” is used to describe a protected data member or method of the class. Note that the python interpreter does not treat it as protected data like other languages, it is only denoted for the programmers since they would be trying to access it using plain name instead of calling it using the respective prefix. For example,
# program to illustrate protected access modifier in a class
# super class
class Student:
# protected data members
_name = None
_roll = None
_branch = None
# constructor
def __init__(self, name, roll, branch):
self._name = name
self._roll = roll
self._branch = branch
# protected member function
def _displayRollAndBranch(self):
# accessing protected data members
print("Roll:", self._roll)
print("Branch:", self._branch)
# derived class
class Geek(Student):
# constructor
def __init__(self, name, roll, branch):
Student.__init__(self, name, roll, branch)
# public member function
def displayDetails(self):
# accessing protected data members of super class
print("Name:", self._name)
# accessing protected member functions of super class
self._displayRollAndBranch()
stu = Student("Alpha", 1234567, "Computer Science")
print(dir(stu))
# protected members and methods can be still accessed
print(stu._name)
stu._displayRollAndBranch()
# Throws error
# print(stu.name)
# stu.displayRollAndBranch()
# creating objects of the derived class
obj = Geek("R2J", 1706256, "Information Technology")
print("")
print(dir(obj))
# calling public member functions of the class
obj.displayDetails()
Output
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass_
_', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_branch', '_displayRollAndBranch', '_name', '_roll']
Alpha
Roll: 1234567
Branch: Computer Science
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_branch', '_displayRollAndBranch', '_name', '_roll', 'displayDetails']
Name: R2J
Roll: 1706256
Branch: Information Technology
In the above program, _name, _roll, and _branch are protected data members and _displayRollAndBranch() method is a protected method of the super class Student. The displayDetails() method is a public member function of the class Geek which is derived from the Student class, the displayDetails() method in Geek class accesses the protected data members of the Student class.
However, we can still access protected members of Student class directly by specifying the correct name of field and method i.e. adding underscore before them since it was declared by that name. We can also see that these declared fields and methods can be called since they are present in the list returned by the dir() function. If we try to access the using plain names such as stu.name and stu.displayRollAndBranch(), we get error since they are not saved by that name. Underscores are mainly used since other characters like “$”, “-“, “&”, etc. cannot be present in variable or function name.
Private Access Modifier:
The members of a class that are declared private are accessible within the class only, private access modifier is the most secure access modifier. Data members of a class are declared private by adding a double underscore ‘__’ symbol before the data member of that class.
# program to illustrate private access modifier in a class
class Geek:
# private members
__name = None
__roll = None
__branch = None
# constructor
def __init__(self, name, roll, branch):
self.__name = name
self.__roll = roll
self.__branch = branch
# private member function
def __displayDetails(self):
# accessing private data members
print("Name:", self.__name)
print("Roll:", self.__roll)
print("Branch:", self.__branch)
# public member function
def accessPrivateFunction(self):
# accessing private member function
self.__displayDetails()
# creating object
obj = Geek("R2J", 1706256, "Information Technology")
print(dir(obj))
print("")
# Throws error
# obj.__name
# obj.__roll
# obj.__branch
# obj.__displayDetails()
# To access private members of a class
print(obj._Geek__name)
print(obj._Geek__roll)
print(obj._Geek__branch)
obj._Geek__displayDetails()
print("")
# calling public member function of the class
obj.accessPrivateFunction()
Output
'_Geek__branch', '_Geek__displayDetails', '_Geek__name', '_Geek__roll', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'accessPrivateFunction']
R2J
1706256
Information Technology
Name: R2J
Roll: 1706256
Branch: Information Technology
Name: R2J
Roll: 1706256
Branch: Information Technology
In the above program, __name, __roll and __branch are private members, __displayDetails() method is a private member function (these can only be accessed within the class) and accessPrivateFunction() method is a public member function of the class Geek which can be accessed from anywhere within the program. The accessPrivateFunction() method accesses the private members of the class Geek.
However, we can still access private members of a class outside the class. We cannot directly call obj.__name, obj.__age, obj.__branch, and obj.__displayDetails() because they throw errors. We can notice that in the list of callable fields and methods, __name is saved as _Geek__name, __age is saved as _Geek__age, __branch is saved as _Geek__branch and __displayDetails() is saved as _Geek__displayDetails(). This conversion is called as name mangling, where the python interpreter automatically converts any member preceded with two underscores to _<class name>__<member name>. Hence, we can still call all the supposedly private data members of a class using the above convention.
Below is a program to illustrate the use of all the above three access modifiers (public, protected, and private) of a class in Python:
# program to illustrate access modifiers of a class
# super class
class Super:
# public data member
var1 = None
# protected data member
_var2 = None
# private data member
__var3 = None
# constructor
def __init__(self, var1, var2, var3):
self.var1 = var1
self._var2 = var2
self.__var3 = var3
# public member function
def displayPublicMembers(self):
# accessing public data members
print("Public Data Member:", self.var1)
# protected member function
def _displayProtectedMembers(self):
# accessing protected data members
print("Protected Data Member:", self._var2)
# private member function
def __displayPrivateMembers(self):
# accessing private data members
print("Private Data Member:", self.__var3)
# public member function
def accessPrivateMembers(self):
# accessing private member function
self.__displayPrivateMembers()
# derived class
class Sub(Super):
# constructor
def __init__(self, var1, var2, var3):
Super.__init__(self, var1, var2, var3)
# public member function
def accessProtectedMembers(self):
# accessing protected member functions of super class
self._displayProtectedMembers()
# creating objects of the derived class
obj = Sub("Geeks", 4, "Geeks!")
# calling public member functions of the class
obj.displayPublicMembers()
obj.accessProtectedMembers()
obj.accessPrivateMembers()
print()
# Can also be accessed using
obj._displayProtectedMembers()
obj._Super__displayPrivateMembers()
print()
# Object can access protected member
print("Object is accessing protected member:", obj._var2)
print("Object is accessing private member:", obj._Super__var3)
# object can not access private member, so it will generate Attribute error
# print(obj.__var3)
Output
Public Data Member: Geeks
Protected Data Member: 4
Private Data Member: Geeks!
Protected Data Member: 4
Private Data Member: Geeks!
Object is accessing protected member: 4
Object is accessing private member: Geeks!
In the above program, the accessProtectedMembers() method is a public member function of the class Sub accesses the _displayProtectedMembers() method which is protected member function of the class Super and the accessPrivateMembers() method is a public member function of the class Super which accesses the __displayPrivateMembers() method which is a private member function of the class Super. Also note that all these access modifiers are not strict like other languages such as C++, Java, C#, etc. since they can still be accessed if they are called by their original or mangled names.