Python - Encapsulation



The principle of Encapsulation is one of the main pillars on which the object-oriented programming paradigm is based. Python takes a different approach to the implementation of encapsulation.

We know that a class is a user-defined prototype for an object. It defines a set of data members and methods, capable of processing the data. According to the principle of data encapsulation, the data members that describe an object are hidden from the environment that is external to the class. They are available for processing to methods defined within the class only. Methods themselves on the other hand are accessible from outside class context. Hence object data is said to be encapsulated by the methods. The result of such encapsulation is that any unwarranted access to the object data is prevented.

Languages such as C++ and Java use access modifiers to restrict access to class members (i.e., variables and methods). These languages have keywords public, protected, and private to specify the type of access.

A class member is said to be public if it can be accessed from anywhere in the program. Private members are allowed to be accessed from within the class only. Usually, methods are defined as public, and instance variables are private. This arrangement of private instance variables and public methods ensures the implementation of encapsulation.

Unlike these languages, Python has no provision to specify the type of access that a class member may have. By default, all the variables and methods in a Python class are public, as demonstrated by the following example.

Example 1

Here, we have an Employee class with instance variables, name and age. An object of this class has these two attributes. They can be directly accessed from outside the class, because they are public.

class Student:
   def __init__(self, name="Rajaram", marks=50):
      self.name = name
      self.marks = marks

s1 = Student()
s2 = Student("Bharat", 25)

print ("Name: {} marks: {}".format(s1.name, s2.marks))
print ("Name: {} marks: {}".format(s2.name, s2.marks))

It will produce the following output

Name: Rajaram marks: 50
Name: Bharat marks: 25

In the above example, the instance variables are initialized inside the class. However, there is no restriction on accessing the value of instance variable from outside the class, which is against the principle of encapsulation.

Although there are no keywords to enforce visibility, Python has a convention of naming the instance variables in a peculiar way. In Python, prefixing name of variable/method with single or double underscore to emulate behavior of protected and private access modifiers.

If a variable is prefixed by a single double underscore (such as "__age"), the instance variable is private, similarly if a variable name is prefixed it with single underscore (such as "_salary")

Example 2

Let us modify the Student class. Add another instance variable salary. Make name private and marks as private by prefixing double underscores to them.

class Student:

   def __init__(self, name="Rajaram", marks=50):
      self.__name = name
      self.__marks = marks
   def studentdata(self):
      print ("Name: {} marks: {}".format(self.__name, self.__marks))
      
s1 = Student()
s2 = Student("Bharat", 25)

s1.studentdata()
s2.studentdata()
print ("Name: {} marks: {}".format(s1.__name, s2.__marks))
print ("Name: {} marks: {}".format(s2.__name, __s2.marks))

When you run this code, it will produce the following output

Name: Rajaram marks: 50
Name: Bharat marks: 25
Traceback (most recent call last):
 File "C:\Python311\hello.py", line 14, in <module>
  print ("Name: {} marks: {}".format(s1.__name, s2.__marks))
AttributeError: 'Student' object has no attribute '__name'

The above output makes it clear that the instance variables name and age, although they can be accessed by a method declared inside the class (the studentdata() method), but since the double underscores prefix makes the variables private, and hence accessing them outside the class is disallowed, raising Attribute error.

Python doesn't block access to private data entirely. It just leaves it for the wisdom of the programmer, not to write any code that access it from outside the class. You can still access the private members by Python's name mangling technique.

Name mangling is the process of changing name of a member with double underscore to the form object._class__variable. If so required, it can still be accessed from outside the class, but the practice should be refrained.

In our example, the private instance variable "__name" is mangled by changing it to the format

obj._class__privatevar

So, to access the value of "__marks" instance variable of "s1" object, change it to "s1._Student__marks".

Change the print() statement in the above program to −

print (s1._Student__marks)

It now prints 50, the marks of s1.

Hence, we can conclude that Python doesn't implement encapsulation exactly as per the theory of object oriented programming. It adapts a more mature approach towards it by prescribing a name convention, and letting the programmer to use name mangling if it is really required to have access to private data in the public scope.

Advertisements