Python Chapter 8: Object-Oriented Programming

Master Classes, Objects, Inheritance, and More | House of Script

Object-Oriented Programming Fundamentals

Object-Oriented Programming (OOP) is a programming paradigm that organizes code into reusable structures called classes and objects. It helps you write cleaner, more maintainable, and scalable code.

Classes

A blueprint or template for creating objects. Defines attributes (data) and methods (behavior).

Objects

Instances of a class. Each object has its own state (attribute values) while sharing the class structure.

Encapsulation

Bundling data and methods together, hiding internal details with private attributes (using _ prefix).

Inheritance

Creating new classes based on existing ones, inheriting attributes and methods while adding new functionality.

Polymorphism

Using the same interface for different data types. Methods with the same name behave differently in different classes.

Abstraction

Hiding complex implementation details and showing only essential features to the user.

1. Classes and Objects

Defining a Class

class Dog: # The __init__ method is the constructor def __init__(self, name, breed): self.name = name # Instance variable self.breed = breed # Instance variable # Instance method def bark(self): return f"{self.name} says Woof!" # Creating objects (instances) dog1 = Dog("Buddy", "Golden Retriever") dog2 = Dog("Max", "Beagle") print(dog1.bark()) # Output: Buddy says Woof! print(dog2.name) # Output: Max
Key Points:
  • self - Refers to the instance itself, must be the first parameter in instance methods
  • __init__ - Constructor method, automatically called when creating an object
  • Instance variables - Unique to each object (self.name, self.breed)

2. Class Variables vs Instance Variables

class Car: # Class variable - shared by all instances wheels = 4 def __init__(self, brand, model): # Instance variables - unique to each object self.brand = brand self.model = model car1 = Car("Toyota", "Camry") car2 = Car("Honda", "Civic") print(car1.wheels) # 4 (class variable) print(car2.wheels) # 4 (same class variable) # Changing class variable affects all instances Car.wheels = 6 print(car1.wheels) # 6

3. Inheritance

Creating Child Classes

class Animal: def __init__(self, name): self.name = name def speak(self): return "Some sound" # Dog inherits from Animal class Dog(Animal): def speak(self): return f"{self.name} barks: Woof!" # Cat inherits from Animal class Cat(Animal): def speak(self): return f"{self.name} meows: Meow!" dog = Dog("Rex") cat = Cat("Whiskers") print(dog.speak()) # Rex barks: Woof! print(cat.speak()) # Whiskers meows: Meow!

4. The super() Function

Used to call methods from the parent class, especially useful in constructors.

class Employee: def __init__(self, name, salary): self.name = name self.salary = salary class Manager(Employee): def __init__(self, name, salary, department): # Call parent constructor using super() super().__init__(name, salary) self.department = department def display_info(self): return f"{self.name} manages {self.department}" mgr = Manager("Alice", 80000, "Engineering") print(mgr.display_info())

5. Encapsulation (Private Attributes)

Use underscore (_) prefix to indicate private attributes (convention, not enforced).

class BankAccount: def __init__(self, owner): self.owner = owner self._balance = 0 # Private attribute (by convention) def deposit(self, amount): if amount > 0: self._balance += amount def get_balance(self): return self._balance account = BankAccount("John") account.deposit(1000) print(account.get_balance()) # 1000

6. Magic Methods (Dunder Methods)

Special methods with double underscores that enable operator overloading and special behavior.

Common Magic Methods:

class Book: def __init__(self, title, author, pages): self.title = title self.author = author self.pages = pages # String representation for users def __str__(self): return f"'{self.title}' by {self.author}" # String representation for developers def __repr__(self): return f"Book('{self.title}', '{self.author}', {self.pages})" # Length of object def __len__(self): return self.pages # Comparison operators def __eq__(self, other): return self.pages == other.pages book = Book("Python Mastery", "Jane Doe", 350) print(book) # 'Python Mastery' by Jane Doe print(len(book)) # 350 print(repr(book)) # Book('Python Mastery', 'Jane Doe', 350)
Common Magic Methods:
  • __init__ - Constructor
  • __str__ - User-friendly string representation
  • __repr__ - Developer-friendly representation
  • __len__ - Define behavior for len()
  • __eq__ - Define equality (==)
  • __lt__ - Less than (<)
  • __add__ - Addition (+)

7. Polymorphism

The ability to use the same interface for different data types.

class Shape: def area(self): pass class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14159 * self.radius ** 2 # Polymorphism in action shapes = [Rectangle(5, 10), Circle(7)] for shape in shapes: print(f"Area: {shape.area()}")

Class Builder

Design your own Python class visually and see the generated code.

Attributes

Methods

Generated Code:

UML Diagram:

Object Inspector

Create objects from predefined classes and inspect their state.

Created Objects:

Inheritance Visualization

Explore how classes inherit from parent classes in a visual hierarchy.

Example 1: Animal Hierarchy

Animal
Dog
Cat
Bird
class Animal: def __init__(self, name): self.name = name def speak(self): pass class Dog(Animal): def speak(self): return "Woof!" class Cat(Animal): def speak(self): return "Meow!"

Example 2: Vehicle Hierarchy

Vehicle
Car
ElectricCar
Motorcycle
Truck
class Vehicle: def __init__(self, brand): self.brand = brand class Car(Vehicle): def __init__(self, brand, doors): super().__init__(brand) self.doors = doors class ElectricCar(Car): def __init__(self, brand, doors, battery): super().__init__(brand, doors) self.battery = battery

Interactive: Build Your Own Tree

Lab Challenge 1: BankAccount Class

Challenge: Create a BankAccount class that manages a customer's bank account.

Requirements:
  • Create a class called BankAccount
  • The constructor should accept owner and initialize balance to 0
  • Implement a deposit(amount) method that adds to the balance
  • Implement a withdraw(amount) method that subtracts from balance (check for sufficient funds)
  • Implement a show_balance() method that displays the current balance

Solution:

class BankAccount: def __init__(self, owner): self.owner = owner self.balance = 0 def deposit(self, amount): self.balance += amount print(f"{self.owner} deposited ${amount}") def withdraw(self, amount): if amount <= self.balance: self.balance -= amount print(f"{self.owner} withdrew ${amount}") else: print("Insufficient funds.") def show_balance(self): print(f"{self.owner}'s balance: ${self.balance}") # Test the class account = BankAccount("Alice") account.deposit(1000) account.withdraw(250) account.show_balance() account.withdraw(1000) # Should show insufficient funds

Lab Challenge 2: Pet Registry

Challenge: Create a Pet Registry system to manage pet information.

Requirements:
  • Create a Pet class with attributes: name, species, age
  • Implement the __str__ method to display pet information nicely
  • Create a list to store multiple pets (registry)
  • Create functions to add pets and view all pets
  • Build a simple menu system for the user

Solution:

class Pet: def __init__(self, name, species, age): self.name = name self.species = species self.age = age def __str__(self): return f"{self.name} ({self.species}), Age: {self.age}" # Pet registry pet_registry = [] def add_pet(name, species, age): pet = Pet(name, species, age) pet_registry.append(pet) print(f"Added: {pet}") def view_pets(): if not pet_registry: print("No pets in registry.") else: print("\n=== Pet Registry ===") for i, pet in enumerate(pet_registry, 1): print(f"{i}. {pet}") # Example usage add_pet("Buddy", "Dog", 3) add_pet("Whiskers", "Cat", 2) add_pet("Tweety", "Bird", 1) view_pets()

Bonus Challenge: Student Grade Manager

Challenge: Create a Student class with grade management.

Requirements:
  • Create a Student class with name and a list of grades
  • Implement add_grade(grade) method
  • Implement get_average() method
  • Implement get_letter_grade() method (A: 90-100, B: 80-89, etc.)
  • Implement __str__ to display student info

Solution:

class Student: def __init__(self, name): self.name = name self.grades = [] def add_grade(self, grade): if 0 <= grade <= 100: self.grades.append(grade) else: print("Grade must be between 0 and 100") def get_average(self): if not self.grades: return 0 return sum(self.grades) / len(self.grades) def get_letter_grade(self): avg = self.get_average() if avg >= 90: return 'A' elif avg >= 80: return 'B' elif avg >= 70: return 'C' elif avg >= 60: return 'D' else: return 'F' def __str__(self): return f"{self.name}: Average = {self.get_average():.1f}, Grade = {self.get_letter_grade()}" # Test student = Student("Bob") student.add_grade(85) student.add_grade(92) student.add_grade(78) print(student)

Object-Oriented Programming Quiz

Test your knowledge of OOP concepts in Python!

Course Home