Skip to content

Programming Fundamentals & OOP

Object-Oriented Programming

Object-oriented programming organizes code around objects: instances of a class that combine state (attributes) with the behavior that operates on that state (methods). A class is the blueprint - it defines what attributes an object will have and what methods it supports - and an object is one specific instance built from that blueprint. Encapsulation is the practice of exposing a controlled interface through methods while keeping the internal representation free to change, so callers depend on behavior rather than on implementation details.

Why it matters

Most libraries and frameworks expose their functionality as classes
Reading OOP code fluently - what's an attribute, what's a method, what self refers to - is a baseline skill for using almost any non-trivial library.
Bundling data with behavior maps to how people already describe a problem
An account object with a balance and a withdraw method reads closer to the real-world concept than a bare number passed between free functions.
Encapsulation limits how far a bug can spread
If every change to a balance has to go through a method that validates it, an invalid balance becomes much harder to create by accident.
It sets up inheritance and polymorphism, and most design patterns, as direct extensions
Once classes exist, sharing behavior between related classes and writing code that works against many types uniformly follow naturally.

Classes and objects

A class defines a blueprint: what attributes each instance will hold and what methods are available on it. init is the method Python calls automatically when a new instance is created, and it is the usual place to set up an object's starting attributes. Every method's first parameter, conventionally named self, refers to the specific instance the method was called on - it is how a method reaches the attributes that belong to that object rather than some other instance of the same class.

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

    def deposit(self, amount):
        self.balance += amount

account = BankAccount('Priya')
account.deposit(50)
print(account.balance)  # 50

Encapsulation: hiding state behind behavior

Encapsulation means callers interact with an object through its methods rather than reaching directly into its attributes. Python has no enforced private keyword the way Java does; a leading underscore on an attribute name is only a convention that signals internal use, and nothing in the language stops code outside the class from reading or changing it anyway. The discipline still matters: routing every change through a method like withdraw lets that method validate the amount and reject a withdrawal that would overdraw the account, something a bare public attribute cannot do on its own.

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

    def withdraw(self, amount):
        if amount > self._balance:
            raise ValueError('insufficient funds')
        self._balance -= amount

account = BankAccount(30)
account.withdraw(50)  # raises ValueError instead of an invalid negative balance

Mistakes people make here

Making every attribute public with no validation
A public balance attribute can be set to any value, including an invalid one, from anywhere in the codebase. Routing changes through a method restores a single place to enforce the rules.
Confusing the class with an instance
A mutable default defined at the class level, rather than inside __init__, is shared by every instance of that class, which surprises people who expect each object to get its own independent copy.
Building 'god classes' that try to do everything
A class that owns validation, persistence, formatting, and business logic all at once becomes hard to change safely, because unrelated responsibilities end up tangled together in the same methods.
Assuming a leading underscore actually enforces privacy in Python
It is a convention read by other programmers and some tools, not an access restriction enforced by the interpreter; code outside the class can still read or write the attribute directly if it chooses to.

Strengths and trade-offs

Where it is strong

  • Groups related state and behavior in one place instead of scattering it across free functions and loose data structures.
  • Encapsulation lets internal representation change later without breaking code that only ever used the public methods.
  • Matches how many problem domains are already described in conversation, which keeps the code readable to people who did not write it.

The trade-offs

  • Not every problem is naturally object-shaped; wrapping a simple data transformation in a class adds ceremony without real benefit.
  • Deep object hierarchies can hide where a value actually gets computed, turning debugging into a chase across several files.
  • Python's encapsulation is convention-based, not enforced, so it protects against accidental misuse more than deliberate misuse.

Who needs this

Anyone reading or writing Python, Java, C#, or most GUI and backend frameworks, since their APIs are built out of classes. It matters less in heavily functional or small-script code, where a handful of functions over plain data does the job without a class in sight.

Questions about object-oriented programming

What's the actual difference between a class and an object?
A class is the definition - the blueprint describing what attributes and methods instances will have. An object is one specific instance built from that blueprint, with its own values for those attributes.
Does Python really support private attributes?
Not in the enforced sense Java or C++ do. A single leading underscore is a convention meaning internal, please don't touch, and a double leading underscore triggers name-mangling that makes accidental access harder but not impossible. Neither actually blocks access the way a compiler-enforced private keyword does.
Do I need OOP for a small script?
Usually not. A script that reads a file, transforms some data, and prints a result is often clearer as a few functions than as a class with one method. Classes earn their cost once you have state that needs to persist across multiple calls, or multiple related pieces of state that always travel together.
What does self actually mean in a method?
It is the specific instance the method was called on, passed automatically as the first argument. Calling a method on an object is equivalent to calling the class's version of that method with the object passed in as the first argument - self is just that object, made available inside the method body.

The primary source

Related concepts

← All concept guides