ASAmol Shukla
Projects
Courses
Prompts
Skills
Contact
Resume
Course Outline
Syllabus Overview

Complete Python Course: From Zero to Professional

Courses/Complete Python Course: From Zero to Professional/Lesson 18: Encapsulation, Properties & Magic Methods
55 mins lesson duration•10 mins read

Lesson 18: Encapsulation, Properties & Magic Methods

Private attributes, @property for smart access, and dunder methods that customize objects.

Encapsulation — Protecting Data

Encapsulation keeps an object's internal state safe by controlling how it is read and changed. Python uses conventions and tools rather than hard enforcement:

Syntax Meaning
self.name Public — anyone can access
self._name "Protected" by convention — don't touch from outside
self.__name Name-mangled — harder to accidentally access
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance    # protected by convention

Mental model: _ is a "staff only" sign. Nothing in Python stops you from entering — but the convention says "you probably shouldn't". __ (double underscore) actually renames the attribute internally, making accidental access fail loudly.

What You'll Learn in This Lesson

  • Protect internal state with private conventions
  • Build smart attribute access with @property
  • Customize objects with magic (dunder) methods

Name Mangling — How __ Works

self.__balance is renamed by Python to self._ClassName__balance at runtime:

class Account:
    def __init__(self):
        self.__secret = 42

acc = Account()
# print(acc.__secret)   # AttributeError! renamed internally
print(acc._Account__secret)   # 42 — the mangled name

The double underscore exists mainly to prevent accidental clashes in inheritance — a child class can't accidentally overwrite the parent's __secret. It is not true security.


@property — Smart Attribute Access

The @property decorator turns a method into an attribute. account.balance reads like plain data but runs code:

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance

    @property
    def balance(self):              # read like an attribute
        return self._balance

    @balance.setter
    def balance(self, amount):      # validate on write
        if amount < 0:
            raise ValueError("Balance cannot be negative")
        self._balance = amount

acc = BankAccount("Amol", 1000)
print(acc.balance)                  # 1000 — no parentheses!
acc.balance = 1500                  # setter validates

Why bother? You can add validation now without breaking existing code that reads acc.balance as a plain attribute.

Mental model: @property is a guarded door. Reading is safe; writing goes through a check (the setter).

The getter-only property (read-only): omit the @setter and the attribute becomes read-only — assignment raises AttributeError. Perfect for computed values:

class Rectangle:
    def __init__(self, w, h):
        self.w, self.h = w, h

    @property
    def area(self):          # computed, read-only
        return self.w * self.h

r = Rectangle(3, 4)
print(r.area)    # 12
# r.area = 99    # AttributeError: can't set attribute

Magic (Dunder) Methods — Customize Your Objects

Double-underscore methods ("dunder" = double underscore) hook your objects into Python's operators and built-ins:

Method Powers Example
__str__ Friendly text for print() print(acc)
__repr__ Exact representation for developers repr(acc)
__eq__ == between objects acc == other
__lt__ Sorting / < sorted(objects)
__len__ len(obj) len(collection)
__getitem__ obj[key] obj[0]
class BankAccount:
    ...
    def __str__(self):
        return f"{self.owner}'s account: Rs.{self._balance}"

    def __eq__(self, other):
        return self._balance == other._balance

print(acc)                    # Amol's account: Rs.1000  (uses __str__)
print(acc == other)           # uses __eq__

str vs repr: str() is for people; repr() is for developers. Implement both; __str__ falls back to __repr__ if missing.

More dunder methods worth knowing:

Method Powers Typical use
__add__ a + b Vector math, money
__len__ len(obj) Collections
__getitem__ obj[i] Indexable objects
__iter__ for x in obj Iterable objects
__call__ obj() Callable objects
__enter__/__exit__ with obj: Context managers (Lesson 20)

Implementing __eq__ and __lt__ together gives you ==, sorting, and min/max for free — professional objects behave exactly like built-ins.


Common Mistakes to Avoid

  • Mistake: Forgetting self in property methods — Fix: properties are methods; self first.
  • Mistake: Naming a property and its backing attribute the same (self.balance + @property balance) — Fix: back it with self._balance.
  • Mistake: Implementing only __eq__ without __hash__ and putting objects in sets — Fix: if you define __eq__, set __hash__ = None or define __hash__ explicitly.
  • Mistake: Expecting __ attributes to be truly private — Fix: it's name mangling, not security; use _ for the convention.
  • Mistake: Returning a value from a setter — Fix: setters must return None.

Professional Tips & Tricks

  • Use @property to add validation without breaking existing code that reads attributes.
  • Always implement __str__ for user-facing classes — debugging and printing become readable.
  • Implement __eq__ and __lt__ together so your objects work with ==, sorting, and min/max.
  • Use getter-only properties for computed values that must stay consistent.
  • Prefer _ conventions over __ for most "private" data — simpler and idiomatic.

Key Takeaways

  • Encapsulation controls access to internal state: _name by convention, __name via name mangling.
  • @property + @setter give attribute-like access with validation.
  • Dunder methods hook objects into Python operators and built-ins.
  • Implement __str__, __repr__, __eq__, and __lt__ for professional objects.
  • Getter-only properties make clean, computed, read-only attributes.

Next up: Dataclasses — modern, boilerplate-free OOP.

Interactive Lesson Code Snippet
# Encapsulation + property + magic methods
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance   # protected by convention

    @property
    def balance(self):
        """Read-only view of the balance."""
        return self._balance

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

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self._balance += amount

    def __str__(self):
        return f"{self.owner}'s account: Rs.{self._balance}"

    def __eq__(self, other):
        return self._balance == other._balance

acc = BankAccount("Amol", 1000)
acc.deposit(500)
print(acc)                       # uses __str__
print("Balance via property:", acc.balance)

acc2 = BankAccount("Riya", 1500)
print("Accounts equal:", acc == acc2)
Language: python

Lesson Code (Python)

# Encapsulation + property + magic methods
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance   # protected by convention

    @property
    def balance(self):
        """Read-only view of the balance."""
        return self._balance

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

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self._balance += amount

    def __str__(self):
        return f"{self.owner}'s account: Rs.{self._balance}"

    def __eq__(self, other):
        return self._balance == other._balance

acc = BankAccount("Amol", 1000)
acc.deposit(500)
print(acc)                       # uses __str__
print("Balance via property:", acc.balance)

acc2 = BankAccount("Riya", 1500)
print("Accounts equal:", acc == acc2)

Console Output

Amol's account: Rs.1500
Balance via property: 1500
Accounts equal: True

Code Visualization Tips

  • 🧠Visualize _private as a 'staff only' room — the convention says do not enter.
  • 🧠Picture @property as a guarded door: reading is safe, writing goes through a check (the setter).
  • 🧠For __str__ vs __repr__, remember: str() is for people, repr() is for developers.

Professional Tips & Tricks

  • ⚡Use @property to add validation without breaking existing code that reads attributes.
  • ⚡Always implement __str__ for user-facing classes — debugging and printing become readable.
  • ⚡Implement __eq__ and __lt__ together so your objects work with ==, sorting, and min/max.

Python Code Judge & Practice Arena

LeetCode Style

Run real Python 3.12 WebAssembly code directly in your browser against automated test suites.

Solved:0 / 20
0 / 370 XP
Challenges:
Problem 1 of 20

Problem 1: 2D Vector Magic Methods 1

Easy+10 XP
Implement class `Vector2D_1(x, y)` supporting `__add__` (vector addition) and `__repr__` returning `'Vector(x, y)'`.
Sample Test Cases:
Input: repr(Vector2D_1(1, 2) + Vector2D_1(3, 4))
Expected: 'Vector(4, 6)'
Input: repr(Vector2D_1(0, 0))
Expected: 'Vector(0, 0)'
main.pyPython 3.12 (WASM)
1
2
3
4
5
6
7
8
9
10
11
12
Press Run Code to test or Submit to verify test cases

Test Your Knowledge

Instant feedback

Quick Check: Lesson 18: Encapsulation, Properties, `__str__`, `__repr__` & Dunder Methods

1 / 20
What is name mangling for double-underscore private attributes? class BankAccount: def __init__(self, balance): self.__balance = balance acc = BankAccount(100) print(acc._BankAccount__balance)

Up next · Continue learning

Dataclasses & Modern OOP

Write less boilerplate with @dataclass, frozen data, and professional OOP best practices.

8 mins read40 mins
Start next lesson
Previous: Inheritance & PolymorphismNext: Dataclasses & Modern OOP
Made withbyAmol Shukla·amolshukla.online
ASAmol Shukla

AI Developer, Trainer & Agentic AI Expert building practical learning systems and real-world AI applications.

Explore

  • Projects
  • Courses
  • Prompts
  • Skills
  • Contact
  • Experience
  • Blogs

Connect

  • Resume
  • Contact
© 2026 Amol Shukla·Created withbyamolshukla.online
Back to top