100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Python

Encapsulation in Python

How Python bundles data and methods together and uses naming conventions and name mangling to signal restricted access.

OOP BasicsIntermediate9 min readJul 7, 2026
Analogies

1. Introduction

Encapsulation is the OOP principle of bundling data (attributes) and the methods that operate on that data into a single unit (a class), while restricting direct outside access to some of that internal state. Its goals are to protect an object's internal consistency and to hide implementation details behind a clean public interface.

🏏

Cricket analogy: Encapsulation is like a team's dressing room strategy being bundled with the players who execute it, kept away from opposition eyes—only the public scoreboard (interface) is visible, protecting the game plan's internal consistency.

Unlike languages such as Java or C++ that enforce access modifiers (private, protected, public) at the compiler level, Python relies on naming conventions and cooperative discipline among developers, backed by a lightweight technical mechanism called name mangling for double-underscore names.

🏏

Cricket analogy: Unlike a franchise with a strict contract clause barring player interviews (enforced), Python is like a team relying on a locker-room code of conduct—players (developers) are trusted not to leak internal strategy, backed by a mild deterrent like a fine (name mangling).

2. Syntax

python
class ClassName:
    def __init__(self):
        self.public_attr = 1        # accessible from anywhere
        self._protected_attr = 2    # convention: internal use, subclasses ok
        self.__private_attr = 3     # name-mangled to _ClassName__private_attr

    @property
    def private_attr(self):         # controlled read access
        return self.__private_attr

    @private_attr.setter
    def private_attr(self, value):  # controlled write access with validation
        if value < 0:
            raise ValueError("must be non-negative")
        self.__private_attr = value

3. Explanation

Python uses three conventions for attribute visibility: a plain name (attr) is public and freely accessible; a single leading underscore (_attr) signals "internal use, please don't touch from outside," but is not enforced by the interpreter; a double leading underscore (__attr) triggers name mangling, where Python internally renames the attribute to _ClassName__attr, making accidental access or overriding from subclasses much less likely (though still possible if you know the mangled name).

🏏

Cricket analogy: A public attribute is like a player's jersey number, visible to all; a single-underscore attribute is like a "do not disturb" note on the dressing room door—respected but not locked; a double-underscore attribute is like a coded locker combination (_ClassName__attr) that's technically crackable but discourages casual access.

The @property decorator is the idiomatic way to expose controlled access to internal state: it lets you keep an attribute name looking like a normal attribute (obj.private_attr) from the outside while actually running validation logic through a getter and setter method underneath.

🏏

Cricket analogy: @property is like a scoreboard displaying "Run Rate: 8.2" as a simple number while underneath a getter recalculates it from overs and runs, and a setter validates that any manual override doesn't produce a negative rate.

Python has no true 'private' attributes enforced by the language — only conventions. A single underscore (_attr) is a polite request not to touch it directly; a double underscore (__attr) triggers name mangling (becoming _ClassName__attr), which discourages accidental access and prevents naming clashes in subclasses, but a determined caller can still access obj._ClassName__attr directly. Encapsulation in Python is fundamentally about discipline and clear signaling, not hard security.

4. Example

python
class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance   # name-mangled to _BankAccount__balance

    @property
    def balance(self):
        return self.__balance

    @balance.setter
    def balance(self, amount):
        if amount < 0:
            raise ValueError("Balance cannot be negative")
        self.__balance = amount


acc = BankAccount("Meera", 1000)
print(acc.balance)

acc.balance = 1500
print(acc.balance)

print(acc._BankAccount__balance)   # mangled name still reachable directly

try:
    acc.balance = -50
except ValueError as e:
    print("Error:", e)

5. Output

text
1000
1500
1500
Error: Balance cannot be negative

6. Key Takeaways

  • Encapsulation bundles data and behavior together and restricts uncontrolled outside access to internal state.
  • Python has no enforced private access — only the _protected and __private naming conventions.
  • Double-underscore names are name-mangled to _ClassName__name, discouraging but not preventing direct access.
  • The @property decorator provides controlled, validated access to internal attributes with normal attribute syntax.
  • Encapsulation in Python relies on convention and cooperative discipline rather than compiler-enforced access control.

Practice what you learned

Was this page helpful?

Topics covered

#Python#PythonProgrammingStudyNotes#Programming#EncapsulationInPython#Encapsulation#Syntax#Explanation#Example#OOP#StudyNotes#SkillVeris