Summary: in this tutorial, you’ll learn about encapsulation and how to use private attributes to accomplish encapsulation in Python.
Introduction to encapsulation in Python
Encapsulation is one of the four fundamental concepts in object-oriented programming including abstraction, encapsulation, inheritance, and polymorphism.
Encapsulation is the packing of data and functions that work on that data within a single object. By doing so, you can hide the internal state of the object from the outside. This is known as information hiding.
A class is an example of encapsulation. A class bundles data and methods into a single unit. And a class provides the access to its attributes via methods.
The idea of information hiding is that if you have an attribute that isn’t visible to the outside, you can control the access to its value to make sure your object is always has a valid state.
Let’s take a look at an example to better understand the encapsulation concept.
Python encapsulation example
The following defines the Counter
class:
class Counter:
def __init__(self):
self.current = 0
def increment(self):
self.current += 1
def value(self):
return self.current
def reset(self):
self.current = 0
Code language: Python (python)
The Counter
class has one attribute called current
which defaults to zero. And it has three methods:
increment()
increases the value of thecurrent
attribute by one.value()
returns the current value of thecurrent
attributereset()
sets the value of thecurrent
attribute to zero.
The following creates a new instance of the Counter
class and calls the increment()
method three times before showing the current value of the counter to the screen:
counter = Counter()
counter.increment()
counter.increment()
counter.increment()
print(counter.value())
Code language: Python (python)
Output:
3
Code language: Python (python)
It works perfectly fine but has one issue.
From the outside of the Counter
class, you still can access the current attribute and change it to whatever you want. For example:
counter = Counter()
counter.increment()
counter.increment()
counter.current = -999
print(counter.value())
Code language: Python (python)
Output:
-999
Code language: Python (python)
In this example, we create an instance of the Counter
class, call the increment()
method twice and set the value of the current attribute to an invalid value -999
.
So how do you prevent the current
attribute from modifying outside of the Counter
class?
That’s why private attributes come into play.
Private attributes
Private attributes can be only accessible from the methods of the class. In other words, they cannot be accessible from outside of the class.
Python doesn’t have a concept of private attributes. In other words, all attributes are accessible from the outside of a class.
By convention, you can define a private attribute by prefixing a single underscore (_):
_attribute
This means that the _attribute should not be manipulated and may have a breaking change in the future.
The following redefines the Counter
class with the current
as a private attribute by convention:
class Counter:
def __init__(self):
self._current = 0
def increment(self):
self._current += 1
def value(self):
return self._current
def reset(self):
self._current = 0
Code language: Python (python)
Name mangling with double underscores
If you prefix an attribute name with double underscores (__
) like this:
__attribute
Python will automatically change the name of the __attribute
to:
_class__attribute
This is called the name mangling in Python.
By doing this, you cannot access the __attribute directly from the outside of a class like:
instance.__attribute
Code language: CSS (css)
However, you still can access it using the _class__attribute name:
instance._class__attribute
Code language: CSS (css)
The following example redefines the Counter class with the __current attribute:
class Counter:
def __init__(self):
self.__current = 0
def increment(self):
self.__current += 1
def value(self):
return self.__current
def reset(self):
self.__current = 0
Now, if you attempt to access __current attribute, you’ll get an error:
counter = Counter()
print(counter.__current)
Code language: PHP (php)
Output:
AttributeError: 'Counter' object has no attribute '__current'
Code language: JavaScript (javascript)
However, you can access the __current attribute as _Counter___current like this:
counter = Counter()
print(counter._Counter__current)
Code language: PHP (php)
Summary
- Encapsulation is the packing of data and methods into a class so that you can hide the information and restrict access from outside.
- Prefix an attribute with a single underscore (
_
) to make it private by convention. - Prefix an attribute with double underscores (
__
) to use the name mangling.