Model real-world things with classes — attributes, methods, the __init__ constructor, inheritance, and encapsulation.
Why: a class is a blueprint for creating objects that bundle data and behaviour together. __init__ is the special setup method that runs when you create an object; self refers to the object being built.
class Dog:
def __init__(self, name, age):
self.name = name # attribute (data on the object)
self.age = age
# create an instance (an object) from the class
rex = Dog('Rex', 3)
print(rex.name) # Rex
print(rex.age) # 3Why: a method is a function that belongs to a class. Its first parameter is always self, which lets it read and change the object’s own attributes.
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
return f'{self.name} says woof!'
rex = Dog('Rex')
print(rex.bark()) # Rex says woof!Why: inheritance lets a new class reuse and extend an existing one. The child class gets the parent’s methods for free and can add or override its own. super() calls the parent’s version.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return 'some sound'
class Cat(Animal): # Cat inherits from Animal
def speak(self): # override the parent method
return f'{self.name} says meow'
c = Cat('Milo')
print(c.speak()) # Milo says meowWhy: encapsulation means keeping an object’s internal data protected and exposing a clean way to use it. A leading underscore (_balance) signals "internal — please don’t touch directly", nudging users toward your methods instead.
class Account:
def __init__(self):
self._balance = 0 # treat as internal
def deposit(self, amount):
if amount <= 0:
raise ValueError('amount must be positive')
self._balance += amount
def balance(self):
return self._balance
acc = Account()
acc.deposit(100)
print(acc.balance()) # 100