Python Encapsulation
The goal of encapsulation is to protect the data inside a class.
A class keeps its data (properties) and methods in one place and decides how code outside the class may reach that data.
As a result, data is harder to change by accident, and the inner workings of the class stay hidden.
Private Properties
Start a property name with a double underscore, __, and Python treats it as private:
Note: Code outside the class has no direct access to a private property.
Example
A class with a private property called __age:
Output: complete Python program
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age # Private property
p1 = Person("Emil", 25)
print(p1.name)
print(p1.__age) # This will cause an error
Get Private Property Value
A getter method gives outside code a way to read a private property:
Example
Read a private property through a getter method:
Output: complete Python program
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
def get_age(self):
return self.__age
p1 = Person("Tobias", 25)
print(p1.get_age())
Set Private Property Value
Changing a private property works the same way, through a setter method.
A setter can also check the new value before storing it:
Example
Change a private property through a setter method:
Output: complete Python program
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
def get_age(self):
return self.__age
def set_age(self, age):
if age > 0:
self.__age = age
else:
print("Age must be positive")
p1 = Person("Tobias", 25)
print(p1.get_age())
p1.set_age(26)
print(p1.get_age())
Why Use Encapsulation?
Encapsulation gives you several benefits:
- Data Protection: Data is safe from accidental changes
- Validation: New values can be checked before they are stored
- Flexibility: The inner code can change while code outside the class keeps working
- Control: You alone decide how data is read and changed
Example
Protect a value and check it with encapsulation:
Output: complete Python program
class Student:
def __init__(self, name):
self.name = name
self.__grade = 0
def set_grade(self, grade):
if 0 <= grade <= 100:
self.__grade = grade
else:
print("Grade must be between 0 and 100")
def get_grade(self):
return self.__grade
def get_status(self):
if self.__grade >= 60:
return "Passed"
else:
return "Failed"
student = Student("Emil")
student.set_grade(85)
print(student.get_grade())
print(student.get_status())
Protected Properties
For protected properties, Python has a convention: start the name with one underscore, _:
Note: This is only a convention, and Python does not enforce it. The _ simply signals to other programmers that the property is meant for internal use.
Example
A class with a protected property:
Output: complete Python program
class Person:
def __init__(self, name, salary):
self.name = name
self._salary = salary # Protected property
p1 = Person("Linus", 50000)
print(p1.name)
print(p1._salary) # Can access, but shouldn't
Private Methods
The double underscore prefix also works on methods, making them private:
Note: A private method behaves like a private property: outside code cannot call it directly. Here, __validate is available only to other methods of the class.
Example
A class with a private method:
Output: complete Python program
class Calculator:
def __init__(self):
self.result = 0
def __validate(self, num):
if not isinstance(num, (int, float)):
return False
return True
def add(self, num):
if self.__validate(num):
self.result += num
else:
print("Invalid number")
calc = Calculator()
calc.add(10)
calc.add(5)
print(calc.result)
# calc.__validate(5) # This would cause an error
Name Mangling
Name mangling is the technique Python uses to make properties and methods private.
A name that starts with double underscores __ is quietly changed: Python puts _ClassName in front of it.
So __age turns into _Person__age.
Note: The mangled name does give you access to a private property (you can use it), but you should avoid it, because it defeats encapsulation's purpose.
Example
Look at a mangled name in action:
Output: complete Python program
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
p1 = Person("Emil", 30)
# This is how Python mangles the name:
print(p1._Person__age) # Not recommended!