Python Encapsulation
In this page:
class ClassName:
def __init__(self):
self._protected = value
self.__private = value
def get_value(self):
return self.__private
What is Encapsulation?
Encapsulation bundles an object's data together with the methods that operate on it and limits how much of that data outside code can touch directly -- the goal is preventing some other part of the program from putting an object into an invalid state by poking at its internals.
Example: What is Encapsulation?
class Account:
def __init__(self, balance):
self.balance = balance
def deposit(self, amount):
self.balance += amount # data and the logic that changes it live together
a = Account(100)
a.deposit(50)
print(a.balance)
Protected Members
A single leading underscore (_balance) is purely a naming convention in Python, not an enforced restriction -- it signals to other developers 'this is internal, please don't touch it directly from outside the class,' but nothing stops them from doing so anyway.
Example: Protected Members
class Account:
def __init__(self, balance):
self._balance = balance # convention: internal use only
a = Account(100)
print(a._balance) # still accessible, but discouraged
Private Members
A double leading underscore (__balance) triggers Python's name-mangling mechanism, which makes the attribute genuinely harder (though not impossible) to access accidentally from outside the class -- this is the closest Python gets to true private attributes.
Example: Private Members
class Account:
def __init__(self, balance):
self.__balance = balance
a = Account(100)
print(a._Account__balance) # name-mangled, hard to access directly
Name Mangling
Under the hood, Python renames a double-underscore attribute from __balance to _ClassName__balance, which is why you'll see that mangled name if you inspect an object's __dict__ -- it's designed to prevent accidental name collisions in subclasses, not to be unbreakable security.
Example: Name Mangling
class Account:
def __init__(self, balance):
self.__balance = balance # double underscore triggers name mangling
a = Account(100)
print(a.__dict__) # shows the mangled name _Account__balance
Getter and Setter Methods
Getter and setter methods (or the @property decorator) give you a controlled doorway to a private attribute -- a setter can validate a new balance isn't negative before accepting it, something plain attribute assignment can't do on its own.
Example: Getter and Setter Methods
class Account:
def __init__(self, balance):
self.__balance = balance
def get_balance(self):
return self.__balance
def set_balance(self, value):
if value >= 0: # validation the setter can enforce
self.__balance = value
a = Account(100)
a.set_balance(200)
print(a.get_balance())
- Thinking a single underscore such as
_balancemakes an attribute private, when it is only a convention that says "internal use". - Accessing a name-mangled attribute as
obj.__secret, which raisesAttributeErrorbecause Python renamed it to_ClassName__secret. - Writing getters and setters for every attribute like Java, when a plain attribute or
@propertyis more Pythonic.
Chapter Quiz — Complete all 11 topics to unlock
0/11 topics done
Complete these topics first: