Home/Learn/Python A–Z/Classes, __init__ & self

Classes, __init__ & self

Beginner
OOP

Classes bundle data and behaviour: __init__ initializes each instance, self is the explicit instance reference, and attributes live per-instance or shared on the class.

Overview

Python OOP strips Java's ceremony: no access modifiers, no getters/setters by default, no "one public class per file". __init__ is the initializer (the object already exists when it runs), and self — the instance — is passed explicitly as the first parameter of every method. The distinction interviews test: instance attributes (set via self, unique per object) vs class attributes (defined on the class, shared by all instances) — and how a mutable class attribute becomes a shared-state bug.

Defining a Class

Attributes are created by assignment in __init__ (no declaration block). Methods are functions whose first parameter is self. Calling obj.method() passes obj as self automatically.

Instance vs class attributes; self demystified
class BankAccount:
    bank_name = "AiCan Bank"            # CLASS attribute — shared

    def __init__(self, owner, balance=0):
        self.owner = owner              # INSTANCE attributes
        self.balance = balance

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

    def __repr__(self):                 # developer-friendly display
        return f"BankAccount({self.owner!r}, {self.balance})"

acc = BankAccount("Asha", 1000)
acc.deposit(500)
print(acc)                    # BankAccount('Asha', 1500)
print(acc.bank_name)          # AiCan Bank — found on the class
print(BankAccount.bank_name)  # same

# obj.method(args) is sugar for Class.method(obj, args)
BankAccount.deposit(acc, 100)   # works — self passed explicitly

The Class-Attribute Trap & classmethod/staticmethod

Reading a missing instance attribute falls back to the class — but assigning through self creates an instance attribute that SHADOWS it. Mutable class attributes are shared state: mutate one, see it everywhere. @classmethod receives the class (great for alternate constructors); @staticmethod receives nothing.

Shared mutable class attribute — a classic bug
class Course:
    students = []                     # BUG WAITING: shared mutable!

    def __init__(self, name):
        self.name = name

    def enroll(self, who):
        self.students.append(who)     # mutates the SHARED list

a, b = Course("Java"), Course("Python")
a.enroll("Ravi")
print(b.students)                     # ['Ravi'] — leaked across instances!
# Fix: create self.students = [] inside __init__

class Pizza:
    def __init__(self, toppings):
        self.toppings = toppings

    @classmethod
    def margherita(cls):              # alternate constructor
        return cls(["tomato", "basil"])

    @staticmethod
    def is_veg(topping):              # utility — no self/cls
        return topping != "chicken"

p = Pizza.margherita()
print(p.toppings, Pizza.is_veg("basil"))

Key Points to Remember

  • 1__init__ initializes an already-created object (creation is __new__, rarely touched)
  • 2self is explicit — obj.method() is Class.method(obj)
  • 3Class attributes are shared; assigning via self shadows them; mutable class attributes are a bug magnet
  • 4@classmethod for alternate constructors (cls), @staticmethod for namespaced utilities

Interview Questions

Sign in to ask Aria
1

Difference between class attributes and instance attributes — show the shadowing behaviour.

MediumTransUnion
2

Why is a mutable class attribute (like a list) dangerous?

MediumAmazon
3

When would you use @classmethod vs @staticmethod?

MediumZomato

Ask Aria about Classes, __init__ & self

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…