OOP Basics

Classes, objects, methods and the four pillars — your first object-oriented programs in Python, Java and C++.

programmingoopclassesobjects

The problem OOP solves

So far, data (variables) and behavior (functions) live separately. Model a bank account that way:

Python
balance = 1000
owner = "Asha"

def deposit(balance, amount):
    return balance + amount

Now model two accounts. Now ten thousand. Now make sure no code ever sets a balance negative, and that every withdrawal checks the limit… With loose variables and functions, nothing enforces that the right data and the right rules travel together.

Object-oriented programming (OOP) fixes this by bundling data and the functions that operate on it into one unit: an object.

Classes and objects

A class is a blueprint; an object (or instance) is one concrete thing built from it. Cookie cutter vs cookies. Architectural plan vs actual houses.

Python
# Python
class BankAccount:
    def __init__(self, owner, balance):   # constructor: runs at creation
        self.owner = owner                 # attributes: the object's data
        self.balance = balance

    def deposit(self, amount):             # method: a function on the object
        self.balance += amount

    def withdraw(self, amount):
        if amount > self.balance:
            return "Insufficient funds"
        self.balance -= amount
        return "OK"

asha = BankAccount("Asha", 1000)    # one object...
rahul = BankAccount("Rahul", 500)   # ...another, fully independent
asha.deposit(250)
print(asha.balance)                  # 1250
print(rahul.balance)                 # 500 — untouched
Java
// Java
public class BankAccount {
    private String owner;       // "private": only this class can touch these
    private double balance;

    public BankAccount(String owner, double balance) {  // constructor
        this.owner = owner;
        this.balance = balance;
    }

    public void deposit(double amount) {
        this.balance += amount;
    }

    public double getBalance() {
        return balance;
    }
}

BankAccount asha = new BankAccount("Asha", 1000);
asha.deposit(250);
C++
// C++
class BankAccount {
private:
    std::string owner;
    double balance;

public:
    BankAccount(std::string owner, double balance)
        : owner(owner), balance(balance) {}

    void deposit(double amount) { balance += amount; }
    double getBalance() const { return balance; }
};

BankAccount asha("Asha", 1000);
asha.deposit(250);

Vocabulary:

  • Constructor — the special function that runs when an object is created (__init__ / class-named method), setting up its starting data.
  • Attribute / field — a variable living inside an object (balance).
  • Method — a function living inside a class, acting on a specific object.
  • self / this — the method's handle on which object it was called on. asha.deposit(250) means "run deposit with self = asha."

You've been using objects all along: in Python, "hello".upper() is a method call on a string object; a list is an object whose methods include append. OOP just lets you define your own kinds.

The four pillars

Interviewers love asking for these by name; here they are at Level-1 depth (Level 5's OOP & SOLID goes to interview depth).

1. Encapsulation — data with a bodyguard

Bundle data with its rules, and hide the raw data so the rules can't be bypassed. In the Java/C++ versions, balance is private: outside code cannot do asha.balance = -5000; it must go through deposit/withdraw, which enforce the rules. The object exposes a small public surface — exactly the interface idea again, at the scale of one object. (Python signals "keep out" by underscore convention, _balance, trusting you; Java/C++ bring a compiler-enforced bodyguard.)

2. Abstraction — usable without understanding

You call car.start(); you don't manage fuel injection. A well-designed class is operated through simple methods that hide genuinely complex machinery — file.read(), model.predict(image). Abstraction is why encapsulation is worth it: hide the internals, and users think at your interface's level, not your implementation's.

3. Inheritance — "is a special kind of"

A class can extend another, inheriting its data and methods and adding its own:

Python
class SavingsAccount(BankAccount):          # SavingsAccount IS A BankAccount
    def __init__(self, owner, balance, rate):
        super().__init__(owner, balance)    # run the parent's constructor
        self.rate = rate

    def add_interest(self):                 # new ability
        self.deposit(self.balance * self.rate)

SavingsAccount gets deposit and withdraw for free. Use inheritance only for true "is-a" relationships — a savings account is an account; a car has an engine (that's composition: put an Engine object inside the Car). Beginners over-inherit; the industry preference is "composition over inheritance," a phrase you'll meet again in Level 5.

4. Polymorphism — same call, different behavior

Code that works on the parent type automatically works on every child, each responding in its own way:

Python
class Cat(Animal):
    def speak(self): return "Meow"

class Dog(Animal):
    def speak(self): return "Woof"

for pet in [Cat(), Dog(), Cat()]:
    print(pet.speak())        # Meow / Woof / Meow — one line, many behaviors

The loop never asks "what kind are you?" — no if/elif chain over types. Add a Parrot class tomorrow and this loop handles it unchanged. That open-for-extension quality is the heart of the design patterns in Level 5.

(This diagram style is UML — the standard sketch language for class design, and the whiteboard language of Level 5 interviews.)

Python Conventions: Underscores & Dunder Methods

Python objects are highly dynamic, and the language relies on specific variable naming conventions and special methods (called "dunders", short for double underscore) to manage object behavior.

1. The Underscore Conventions (_ vs __)

  • Single Underscore (_variable): A convention indicating that a variable or method is intended for internal use only (protected/private). Python does not prevent outside access, but developers respect it as a "keep out" sign.
  • Double Underscores (__variable): Triggers name mangling, where Python automatically renames the variable to _ClassName__variable to prevent accidental overriding in child classes.
Python
class Node:
    def __init__(self, val):
        self.val = val
        self._internal_id = 123  # internal use only (protected convention)
        self.__private_key = "abc" # name mangled (private)

n = Node(5)
print(n._internal_id)   # 123 (works, but frowned upon)
# print(n.__private_key) # ❌ Raises AttributeError (name is mangled!)
print(n._Node__private_key) # "abc" (how to access the mangled name)

2. Dunder Methods (Magic Methods)

Dunder methods are special pre-defined methods that start and end with double underscores. They allow your custom classes to integrate with Python's built-in syntax (operators, functions):

  • __str__ and __repr__ (Print representation): Determines what is displayed when you print an object or view it in a list. In DSA, defining __repr__ makes debugging custom node structures (trees, lists) incredibly easy.
  • __len__ (Sizing): Allows calling len(obj) on your custom collection.
  • __lt__ (Less Than): Defines the < comparison logic. This is critical in DSA when pushing custom objects into a min-heap or priority queue using heapq.
Python
import heapq

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    # Debug representation
    def __repr__(self):
        return f"{self.name}({self.score})"

    # Less-than comparison (compares students by score)
    def __lt__(self, other):
        return self.score < other.score

s1 = Student("Asha", 91)
s2 = Student("Rahul", 84)

print(s1) # Prints: Asha(91) - uses __repr__

# Heap queue uses < under the hood to organize elements
heap = []
heapq.heappush(heap, s1)
heapq.heappush(heap, s2)

# Pops student with smallest score (Rahul) because we defined __lt__
print(heapq.heappop(heap)) # Prints: Rahul(84)

Industry perspective

  • Java is OOP-mandatory (all code lives in classes); Python and C++ are multi-paradigm — you choose when objects help. Knowing when is the skill: a 10-line script needs no classes; a banking system with 400 entity types can't live without them.
  • Every major framework hands you its power through classes you extend or objects you configure — Android Activities, Spring services (Level 7), React components historically (Level 8).
  • LLD interviews (Level 5) are OOP interviews: "design a parking lot" means "show me your classes, their responsibilities and relationships." What you learned here — plus SOLID and patterns — is exactly that round.

Common beginner mistakes

  • Classes for everything. A function returning a value doesn't need a Calculator class. Reach for a class when data and its rules belong together and you'll need several of them.
  • The God object — one Manager class holding all the data and methods. That's procedural code in an OOP costume. Many small classes, one responsibility each.
  • Public everything — exposing every field defeats encapsulation: any code can now break your invariants, and you can never change internals without breaking users.
  • Deep inheritance towersA > B > C > D > E where a change at the top breaks everything below. Prefer shallow trees and composition.
  • Forgetting objects are referencesaccount2 = account1 aliases one object (as with lists); both names see every change.

Think it through

The pillar interviewers probe hardest is polymorphism — because it kills the if/elif-over-types code beginners write by reflex. Reason through the payoff before revealing.

Think it through: Total area of mixed shapesBeginner — why polymorphism beats a type-switch0/5 stages

PROBLEMYou have a list of mixed shapes (circles, rectangles, …) and must sum their areas. A new shape type is added every month. Design it so adding a shape never forces you to edit the summing code.

  1. 1

    The naive approach

    If each shape just stored a 'kind' string, how would total_area look — and what breaks?

  2. 2

    The OOP insight

    What if each shape knew how to compute its own area?

    unlocks after the stage above
  3. 3

    Give them a common type

    How do the shapes agree to share the area() interface?

    unlocks after the stage above
  4. 4

    Code it

    Base with area(), two subclasses, and a sum loop that asks no questions.

    unlocks after the stage above
  5. 5

    The payoff

    A Triangle ships next month. What changes?

    unlocks after the stage above

Check yourself

Check yourself0/4 answered

1. Encapsulation means:

2. Polymorphism lets a loop call pet.speak() over cats and dogs with no if/elif over types. The main benefit is:

3. When should you use inheritance rather than composition?

4. `account2 = account1`, where account1 is an object. What's true?

Interview perspective

Practice

Beginner

  1. Write a Rectangle class: constructor takes width and height; methods area() and perimeter(). Create three rectangles and print their areas.
  2. Add an is_square() method returning a boolean. Then explain: why is this better as a method than as a free function taking width and height?

Intermediate

  1. Build a Library system: a Book class (title, author, checked_out) and a Library class holding a list of books with methods add_book, checkout(title), return_book(title). Enforce: a checked-out book can't be checked out again.
  2. Make EBook inherit from Book with a file_size attribute, and override its describe() method. Loop over a mixed list printing descriptions — polymorphism in action.

Advanced

  1. Write the BankAccount in Java or C++ with truly private balance, and try to set it directly from outside — read the compiler error carefully. Then add a transfer(other, amount) method that's safe even when funds are insufficient. Which earlier concept (transactions, from Level 0) is this a tiny version of?

This completes the Level 1 starter set. Exceptions, collections and file handling land next in this section — then Level 2's data structures await. For the interview-grade version of today's ideas, see OOP & SOLID.