Encapsulation

An objects variables should not always be directly accessible.

To prevent accidental change, an objects variables can sometimes only be changed with an objects methods. Those type of variables are private variables.

Important Note:

In C++ and Java, things are pretty straight-forward. There are 3 magical and easy to remember access modifiers, that will do the job (public, protected and private). But there’s no such a thing in Python. That might be a little confusing at first, but it’s possible too. We’ll have look at how do it right in Python.

Python doesn’t have any mechanisms, that would effectively restrict you from accessing a variable or calling a member method. All of this is a matter of culture and convention. In PYTHON, we can easily bypass protected/private variables/methods.

  1. Protected Variables

  2. Private Variables

  3. Protected Methods

  4. Private Methods

Protected Variables:

Protected members (in C++ and JAVA) are those members of the class which cannot be accessed outside the class but can be accessed from within the class and it’s subclasses. To accomplish this in Python, just follow the convention by prefixing the name of the member by a single underscore “_”.

# Creating a base class 
class Base: 
    def __init__(self): 
          
        # Protected member 
        self._a = 2
  
# Creating a derived class     
class Derived(Base): 
    def __init__(self): 
          
        # Calling constructor of 
        # Base class 
        Base.__init__(self)  
        print("Calling protected member of base class: ") 
        print(self._a) 
          
obj1 = Base() 
  
# Calling protected member 
# Outside class will  result in  
# AttributeError 
print(obj1.a) 
  
obj2 = Derived() 

AttributeError Traceback (most recent call last)

in 25 # Outside class will result in 26 # AttributeError ---> 27 print(obj1.a) 28 29 obj2 = Derived()

AttributeError: 'Base' object has no attribute 'a'

Bypassing Protected Variable

Example 2:

# Creating a base class 
class Base: 
    def __init__(self): 
          
        # Protected member 
        self._a = 2
  
# Creating a derived class     
class Derived(Base): 
    def __init__(self): 
          
        # Calling constructor of 
        # Base class 
        Base.__init__(self)  
        print("Calling protected member of base class: ") 
        print(self._a) 
          
obj1 = Base() 
  
 
print(obj1._a) 
  
obj2 = Derived() 

2

Calling protected member of base class:

2

Python doesn’t have any mechanisms, that would effectively restrict you from accessing a variable or calling a member method. All of this is a matter of culture and convention

Private Variable

Example 1

# Creating a base class 
class Base: 
    def __init__(self): 
        self.__a = 2
  
# Creating a derived class     
class Derived(Base): 
    
    def __init__(self): 
        Base.__init__(self)  
        print("Calling private member of base class will give error") 
        print(self.__a) 
          
obj1 = Base() 
  
print(obj1.a) 
  
obj2 = Derived()

AttributeError Traceback (most recent call last)

in 14 obj1 = Base() 15 ---> 16 print(obj1.a) 17 18 obj2 = Derived()

AttributeError: 'Base' object has no attribute 'a'

ByPassing Private Variable

Example 2:

# Creating a base class 
class Base: 
    def __init__(self): 
        self.__a = 2
  
# Creating a derived class     
class Derived(Base): 
    
    def __init__(self): 
        Base.__init__(self)  
        print("Calling private member of base class will give error") 
        print(self.__a) 
          
obj1 = Base() 
  
print(obj1._Base__a) 
  
obj2 = Derived() 

2

Calling private member of base class will give error

2

Protected Method:

Example 1:

class Car:

    def __init__(self):
        self._updateSoftware()

    def drive(self):
        print('driving')

    def _updateSoftware(self):
        print('updating software')

redcar = Car()
redcar.drive()
redcar.updateSoftware() 

updating software

driving

AttributeError Traceback (most recent call last)

in 12 redcar = Car() 13 redcar.drive() ---> 14 redcar.updateSoftware()

AttributeError: 'Car' object has no attribute 'updateSoftware'

ByPassing Protected Method:

class Car:

    def __init__(self):
        self._updateSoftware()

    def drive(self):
        print('driving')

    def _updateSoftware(self):
        print('updating software')

redcar = Car()
redcar.drive()
redcar._updateSoftware() 

updating software

driving

updating software

Private Method:

class Car:

    def __init__(self):
        self.__updateSoftware()

    def drive(self):
        print('driving')

    def __updateSoftware(self):
        print('updating software')
        
redcar = Car()
redcar.drive()
redcar.__updateSoftware() 

updating software

driving

AttributeError Traceback (most recent call last)

in 12 redcar = Car() 13 redcar.drive() ---> 14 redcar.__updateSoftware()

AttributeError: 'Car' object has no attribute '__updateSoftware'

Bypassing Private Method:

class Car:

    def __init__(self):
        self.__updateSoftware()

    def drive(self):
        print('driving')

    def __updateSoftware(self):
        print('updating software')
        
redcar = Car()
redcar.drive()
redcar._Car__updateSoftware() 

updating software

driving

updating software

Encapsulation example

Python does not have the private keyword, unlike some other object oriented languages, but encapsulation can be done.

Instead, it relies on the convention: a class variable that should not directly be accessed should be prefixed with an underscore.

Example 1:

class Robot(object):
   def __init__(self):
      self.a = 123
      self._b = 123
      self.__c = 123

obj = Robot()
print(obj.a)
print(obj._b)
print(obj.__c)

123

123

Traceback (most recent call last):

File "test.py", line 10, in <module>

print(obj.__c)

AttributeError: 'Robot' object has no attribute 'c'

So what’s with the underscores and error?

A single underscore: Private variable, it should not be accessed directly. But nothing stops you from doing that (except convention).

A double underscore: Private variable, harder to access but still possible.

Both are still accessible: Python has private variables by convention.

Getters and setters

Private variables are intended to be changed using getter and setter methods. These provide indirect access to them:

Example 2:

class Robot(object):
   def __init__(self):
      self.__version = 22

   def getVersion(self):
      print(self.__version)

   def setVersion(self, version):
      self.__version = version

obj = Robot()
obj.getVersion()
obj.setVersion(23)
obj.getVersion()
print(obj.__version)

This then outputs the variables values:

22

23

23

Example 3 :

#!/usr/bin/python3
class JustCounter:
    __secretCount = 0

    def count(self):
        self.__secretCount += 1
        print (self.__secretCount)
    
counter = JustCounter()
counter.count()
counter.count()
print (counter._JustCounter__secretCount)
print (JustCounter._JustCounter__secretCount)
print (counter.__secretCount)

1

2

2

0

AttributeError Traceback (most recent call last)

<ipython-input-32-0677a9635e82> in <module>

in 13 print (counter._JustCountersecretCount)

14 print (JustCounter._JustCountersecretCount)

---> 15 print (counter.__secretCount)

AttributeError: 'JustCounter' object has no attribute '__secretCount'

Example 4:

class MyClass: 
  
    # Hidden member of MyClass 
    __hiddenVariable = 0
    
    # A member method that changes  
    # __hiddenVariable  
    def add(self, increment): 
        self.__hiddenVariable += increment 
        print (self.__hiddenVariable) 
   
# Driver code 
myObject = MyClass()      
myObject.add(2) 
myObject.add(5) 
  
# This line causes error 
print (myObject.__hiddenVariable) 

2

7

Traceback (most recent call last):

File "filename.py", line 13, in

print (myObject.hiddenVariable)

AttributeError: MyClass instance has no attribute 'hiddenVariable'

In the above program, we tried to access hidden variable outside the class using object and it threw an exception.

We can access the value of hidden attribute by a tricky syntax:

# A Python program to demonstrate that hidden 
# members can be accessed outside a class 
class MyClass: 
  
    # Hidden member of MyClass 
    __hiddenVariable = 10
  
# Driver code 
myObject = MyClass()      
print(myObject._MyClass__hiddenVariable) 

10

Example 6:

class MyClass: 
  
    # Hidden member of MyClass 
    __hiddenVariable = 0
    
    # A member method that changes  
    # __hiddenVariable  
    def add(self, increment): 
        self.__hiddenVariable += increment 
        print (self.__hiddenVariable) 
   
# Driver code 
myObject = MyClass()      
myObject.add(2) 
myObject.add(5) 
  

print (myObject._MyClass__hiddenVariable) 
print (MyClass._MyClass__hiddenVariable) 

2

7

7

0

Last updated