Introduction to OOP

What is Object-Oriented Programming?

Object-Oriented Programming (OOP) is a programming paradigm that organizes code around objects rather than functions and logic. Objects combine data (attributes) and behavior (methods) into a single unit.

Think of it like modeling real-world things:

  • A Car has attributes (color, brand, speed) and behaviors (accelerate, brake, turn)
  • A BankAccount has attributes (balance, owner) and behaviors (deposit, withdraw)
  • A Student has attributes (name, grade) and behaviors (study, take_exam)

Why Use OOP?

OOP provides several key benefits:

  • Organization: Group related data and functions together
  • Reusability: Create templates (classes) that can be reused
  • Maintainability: Changes to one class don't affect others
  • Abstraction: Hide complexity behind simple interfaces

Classes vs Objects

Understanding the difference is crucial:

CLASS: Dog
• name
• breed
• age
• bark()
• fetch()
↓ Creates instances ↓
OBJECT: buddy
name = "Buddy"
breed = "Golden"
age = 3
OBJECT: max
name = "Max"
breed = "Husky"
age = 5

Class: A blueprint or template (defines what a Dog is)
Object: A specific instance of a class (an actual dog with real values)

Key Concept

A class is like a cookie cutter - it defines the shape. An object is like a cookie - it's the actual thing created from the template. You can make many cookies (objects) from one cutter (class).

LAB 1 Understanding Classes vs Objects

Run this code to see the difference between a class and its objects.

main.py
Output
Click "Run" to see output...
Hint: Notice how Dog is the class (template), while buddy and max are objects (instances). Each has its own name and breed.
✓ Excellent! You understand the difference between classes and objects.

Classes & Objects

Defining a Class

Use the class keyword to define a class. Class names use PascalCase by convention:

Basic Class Definition
class Student: """A class representing a student.""" def __init__(self, name, grade): """Initialize student with name and grade.""" self.name = name # Instance attribute self.grade = grade # Instance attribute

The __init__ Method

The __init__ method is a special method called a constructor. It's automatically called when you create a new object:

Constructor Example
class BankAccount: def __init__(self, owner, balance=0): self.owner = owner self.balance = balance print(f"Account created for {owner}") # Creating an object calls __init__ automatically account = BankAccount("Alice", 1000) # Output: Account created for Alice # Use default balance account2 = BankAccount("Bob") print(account2.balance) # Output: 0
Key Concept: self

self refers to the current instance of the class. It's how the object accesses its own attributes and methods. Always include self as the first parameter in instance methods.

Creating and Using Objects

Working with Objects
class Rectangle: def __init__(self, width, height): self.width = width self.height = height # Create objects rect1 = Rectangle(10, 5) rect2 = Rectangle(3, 7) # Access attributes print(rect1.width) # 10 print(rect2.height) # 7 # Modify attributes rect1.width = 15 print(rect1.width) # 15
LAB 2 Creating Your Own Class

Create a Person class with name and age attributes. Create two Person objects and print their information.

main.py
Output
Click "Run" to see output...
Hint: Define the class with class Person:, add __init__(self, name, age), and assign self.name = name and self.age = age.
✓ Great job! You've created your first class from scratch.

Methods & self

Instance Methods

Methods are functions defined inside a class. They define the behaviors of objects:

Defining Methods
class BankAccount: def __init__(self, owner, balance=0): self.owner = owner self.balance = balance def deposit(self, amount): """Add money to the account.""" self.balance += amount return f"Deposited ${amount}. New balance: ${self.balance}" def withdraw(self, amount): """Remove money from the account.""" if amount > self.balance: return "Insufficient funds!" self.balance -= amount return f"Withdrew ${amount}. New balance: ${self.balance}" def get_balance(self): """Return current balance.""" return self.balance

Calling Methods

Call methods on objects using dot notation:

Using Methods
# Create an account account = BankAccount("Alice", 100) # Call methods print(account.deposit(50)) # Deposited $50. New balance: $150 print(account.withdraw(30)) # Withdrew $30. New balance: $120 print(account.get_balance()) # 120 print(account.withdraw(200)) # Insufficient funds!
Common Mistake

Forgetting self is the #1 OOP mistake! Every instance method must have self as its first parameter, and use self.attribute to access instance data.

Methods that Return Values vs Modify State

Methods can either return information or modify the object's state:

Different Method Types
class Counter: def __init__(self): self.count = 0 # Modifies state (mutator) def increment(self): self.count += 1 # Returns information (accessor) def get_count(self): return self.count # Does both def reset(self): old_count = self.count self.count = 0 return old_count # Return what it was
LAB 3 Building a Complete Class with Methods

Create a Rectangle class with methods to calculate area and perimeter. Then test it with different rectangles.

main.py
Output
Click "Run" to see output...
Hint: Area = width × height, Perimeter = 2 × (width + height). Make sure each method has self and returns the calculated value.
✓ Excellent! You've mastered creating methods for your classes.

Inheritance

What is Inheritance?

Inheritance allows a class to inherit attributes and methods from another class. This promotes code reuse and creates logical hierarchies:

Animal (Parent)
• name
• age
• eat()
• sleep()
↓ inherits from ↓
Dog (Child)
+ breed
+ bark()
+ fetch()
Cat (Child)
+ color
+ meow()
+ purr()

Creating Child Classes

Put the parent class name in parentheses to inherit from it:

Inheritance Syntax
# Parent class class Animal: def __init__(self, name, age): self.name = name self.age = age def speak(self): return "Some generic sound" def describe(self): return f"{self.name} is {self.age} years old" # Child class inherits from Animal class Dog(Animal): def __init__(self, name, age, breed): super().__init__(name, age) # Call parent's __init__ self.breed = breed def speak(self): # Override parent method return "Woof!" def fetch(self): # New method specific to Dog return f"{self.name} fetches the ball!"
Key Concept: super()

super() gives you access to the parent class. Use super().__init__() in child classes to run the parent's constructor first, then add your own initialization.

Method Overriding (Polymorphism)

Child classes can override parent methods to provide specialized behavior:

Polymorphism Example
class Cat(Animal): def speak(self): # Override return "Meow!" # Both are Animals, but speak() behaves differently dog = Dog("Buddy", 3, "Golden") cat = Cat("Whiskers", 2) print(dog.speak()) # Woof! print(cat.speak()) # Meow! # Both inherit describe() from Animal print(dog.describe()) # Buddy is 3 years old print(cat.describe()) # Whiskers is 2 years old
LAB 4 Creating an Inheritance Hierarchy

Create a Vehicle parent class and Car and Motorcycle child classes. Each should have a describe() method.

main.py
Output
Click "Run" to see output...
Hint: Use class Car(Vehicle): to inherit. In __init__, call super().__init__(brand, year) first, then add the child-specific attribute.
✓ Fantastic! You've created an inheritance hierarchy.

Encapsulation & Special Methods

Encapsulation

Encapsulation means bundling data with the methods that operate on it, and restricting direct access to some components. In Python, we use naming conventions:

Access Conventions
class BankAccount: def __init__(self, owner, balance): self.owner = owner # Public attribute self._balance = balance # Protected (convention) self.__pin = "1234" # Private (name mangling) def get_balance(self): """Getter method for balance.""" return self._balance def deposit(self, amount): """Safely modify balance.""" if amount > 0: self._balance += amount

• public - accessible anywhere
• _protected - "please don't access directly" (convention)
• __private - name mangled to _ClassName__attr

Special Methods (Dunder Methods)

Python has special methods with double underscores that customize class behavior:

Common Special Methods
class Book: def __init__(self, title, pages): self.title = title self.pages = pages def __str__(self): """String representation for users.""" return f"'{self.title}' ({self.pages} pages)" def __repr__(self): """String representation for developers.""" return f"Book('{self.title}', {self.pages})" def __len__(self): """Return length (pages).""" return self.pages def __eq__(self, other): """Check equality.""" return self.title == other.title book = Book("Python Guide", 350) print(book) # 'Python Guide' (350 pages) print(len(book)) # 350
Key Concept

Special methods let your classes work with Python's built-in functions. __str__ works with print(), __len__ works with len(), __eq__ works with ==.

LAB 5 Implementing Special Methods

Create a Point class with __str__, __eq__, and a method to calculate distance from origin.

main.py
Output
Click "Run" to see output...
Hint: The distance formula is √((x₂-x₁)² + (y₂-y₁)²). Use math.sqrt() and **2 for squaring. For __eq__, compare both x and y coordinates.
✓ Excellent! You've mastered special methods.

Chapter Summary

What You've Learned

Congratulations! You've completed the final chapter. Here's what you've mastered:

  • OOP Fundamentals: Classes vs objects, why OOP matters
  • Classes & Objects: class keyword, __init__, creating instances
  • Methods & self: Instance methods, the purpose of self
  • Inheritance: Parent/child classes, super(), method overriding
  • Encapsulation: Public, protected, private attributes
  • Special Methods: __str__, __repr__, __eq__, __len__

OOP Quick Reference

Complete Class Template
class ClassName(ParentClass): """Class docstring.""" class_attribute = "shared" # Shared by all instances def __init__(self, param1, param2): super().__init__(param1) # If inheriting self.param1 = param1 # Instance attribute self._protected = param2 # Protected convention def __str__(self): return f"ClassName({self.param1})" def instance_method(self): """Method that uses self.""" return self.param1 @classmethod def class_method(cls): """Method that uses cls (the class).""" return cls.class_attribute @staticmethod def static_method(): """Method that doesn't use self or cls.""" return "I'm independent!"
The Four Pillars of OOP

1. Encapsulation: Bundle data and methods, control access
2. Abstraction: Hide complexity, expose simple interfaces
3. Inheritance: Create new classes based on existing ones
4. Polymorphism: Same interface, different behaviors

Python Course Complete!

You've completed all 8 chapters. Time for the final exam!