Classes, Objects, and the __init__ Constructor
📂 Phase 4: OOP Concepts, File Handling, Exception Handling (Days 16–20) · Python ProgrammingA class is a blueprint that defines the attributes (data) and methods (behavior) that its objects will have. An object is a concrete instance created from that blueprint, holding its own actual data — everything you have used so far, from strings to lists, is itself an object built from a class.
Defining a Class
class Car:
pass # an empty class body — placeholder for now
my_car = Car() # creating an OBJECT (instance) of the Car class
print(type(my_car)) #
The __init__ Constructor
__init__ is a special method that runs automatically every time a new object is created — used to set up the object's initial attributes. It is the closest equivalent to a constructor in other languages.
class Car:
def __init__(self, color, model):
self.color = color
self.model = model
self.speed = 0
my_car = Car("Red", "Civic")
print(my_car.color) # Red
print(my_car.model) # Civic
Understanding self
self refers to the specific object the method is being called on — it must be the first parameter of every instance method, though Python passes it automatically; you never type it yourself when calling the method.
class Car:
def __init__(self, model):
self.model = model # self.model belongs to THIS object specifically
def honk(self):
print(f"{self.model} says: Beep beep!")
car1 = Car("Civic")
car2 = Car("Mustang")
car1.honk() # Civic says: Beep beep!
car2.honk() # Mustang says: Beep beep!
self is not a Python keyword — it is just a strong, near-universal convention. You could technically name it anything, but every Python developer expects to see self, and deviating from it will confuse anyone reading your code.
Instance Methods
class Car:
def __init__(self, model, speed=0):
self.model = model
self.speed = speed
def accelerate(self, amount):
self.speed += amount
print(f"{self.model} is now going {self.speed} km/h")
my_car = Car("Civic")
my_car.accelerate(20) # Civic is now going 20 km/h
my_car.accelerate(15) # Civic is now going 35 km/h
Multiple Independent Objects
Every object created from a class has its own separate copy of the instance attributes — modifying one object's data never affects another object of the same class.
car1 = Car("Civic")
car2 = Car("Mustang")
car1.accelerate(30)
print(car1.speed) # 30
print(car2.speed) # 0 — completely unaffected by car1
Class Attributes vs Instance Attributes
| Type | Defined Where | Shared Across Objects? |
|---|---|---|
| Class attribute | Directly inside the class, outside any method | Yes — same value for every object unless overridden |
| Instance attribute | Inside __init__, using self. | No — each object has its own independent copy |
class Car:
wheels = 4 # class attribute — same for every Car object
def __init__(self, model):
self.model = model # instance attribute — unique per object
car1 = Car("Civic")
car2 = Car("Mustang")
print(car1.wheels, car2.wheels) # 4 4 — shared class attribute
The __str__ Method — Controlling How an Object Prints
class Car:
def __init__(self, model):
self.model = model
def __str__(self):
return f"Car(model={self.model})"
my_car = Car("Civic")
print(my_car) # Car(model=Civic) — without __str__, this would print something unhelpful
# like <__main__.Car object at 0x7f...>
Interview tip: A common question is "What happens if you don't define __init__?" — Python silently uses a default constructor that takes no arguments and does nothing; you would then have to manually assign every attribute on each object after creation, which is rarely practical for anything beyond a trivial class.