Skip to content

Chapter 7: Object-Oriented Programming

Classes and Objects

If a "class" is a cookie cutter, then an "object" is the cookie pressed out from that cutter. The cutter defines the shape, but each cookie is an independent individual.

bash
# Define a "Dog" class
class Dog:
    # Class variable: shared by all instances
    species = "Canine"

    # __init__ is the constructor, automatically called when creating an object
    def __init__(self, name, age, breed):
        # Instance variables: unique to each instance
        self.name = name
        self.age = age
        self.breed = breed

    # Instance method
    def bark(self):
        return f"🐕 {self.name} says: Woof!"

    def info(self):
        return f"{self.name} is a {self.breed}, {self.age} years old"

# Create objects (instantiate)
dog1 = Dog("Rex", 3, "Golden Retriever")
dog2 = Dog("Buddy", 5, "Corgi")

print(dog1.bark())
print(dog2.info())
print(f"Breed: {dog1.species}")
bash
🐕 Rex says: Woof!
Buddy is a Corgi, 5 years old
Breed: Canine

Inheritance

bash
# Parent class
class Animal:
    def __init__(self, name, sound):
        self.name = name
        self.sound = sound

    def speak(self):
        return f"{self.name} says: {self.sound}!"

# Child class inherits from parent
class Cat(Animal):
    def __init__(self, name, indoor=True):
        super().__init__(name, sound="Meow")  # Call parent constructor
        self.indoor = indoor

    def purr(self):  # Method unique to child class
        return f"{self.name} is purring... purr purr 😺"

class Duck(Animal):
    def __init__(self, name):
        super().__init__(name, sound="Quack")

    def swim(self):
        return f"{self.name} is swimming 🏊"

cat = Cat("Whiskers")
duck = Duck("Donald")

print(cat.speak())  # Inherited method
print(cat.purr())   # Child class method
print(duck.speak())
print(duck.swim())
bash
Whiskers says: Meow!
Whiskers is purring... purr purr 😺
Donald says: Quack!
Donald is swimming 🏊

Polymorphism

bash
class Shape:
    def area(self):
        raise NotImplementedError("Subclasses must implement the area method")

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        import math
        return math.pi * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

# Polymorphism: same interface, different behavior
shapes = [Circle(5), Rectangle(4, 6), Circle(3)]
for shape in shapes:
    print(f"{shape.__class__.__name__} area = {shape.area():.2f}")
bash
Circle area = 78.54
Rectangle area = 24.00
Circle area = 28.27

Encapsulation and Magic Methods

bash
# Encapsulation: use _ and __ to control access
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner          # Public attribute
        self._bank = "Python Bank"  # Convention for private (just advisory, still accessible)
        self.__balance = balance    # Name mangling (more strict)

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return f"Deposited {amount}, balance is {self.__balance}"
        return "Deposit amount must be positive"

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            return f"Withdrew {amount}, balance is {self.__balance}"
        return "Insufficient balance"

    # Magic methods: give objects special behavior
    def __str__(self):
        # Called by print()
        return f"[{self.owner}'s Account] Balance: {self.__balance}"

    def __repr__(self):
        # Called during debugging
        return f"BankAccount('{self.owner}', {self.__balance})"

    def __len__(self):
        # Called by len(), returns the "digit count" of the balance
        return len(str(self.__balance))

account = BankAccount("Alice", 1000)
print(account)
print(account.deposit(500))
print(account.withdraw(200))
print(repr(account))
print(f"Balance digits: {len(account)}")
bash
[Alice's Account] Balance: 1000
Deposited 500, balance is 1500
Withdrew 200, balance is 1300
BankAccount('Alice', 1300)
Balance digits: 4