Module 14 - Introducing Object-Oriented Programming Header

Module 14 - Introducing Object-Oriented Programming

Introducing OOP

Imagine you have a toolbox filled with different types of tools – hammers, screwdrivers, pliers, and wrenches. Each tool has a specific purpose and set of actions it can perform. In the real world, objects are like those tools, and OOP is a way of organizing and managing them in programming.

What is an Object?

An object is a fundamental concept in OOP. It represents a specific thing or entity that has both data (attributes) and behavior (actions/methods). Just like a tool in your toolbox, an object in programming could be anything – a person, an animal, a car, or even an abstract concept like a book or a bank account.

Classes: Blueprints for Objects

A class is like a blueprint or template that defines the structure and behavior of an object. It serves as a guide for creating objects with common characteristics. For example, if you want to create multiple cars in a computer program, you would define a class called "Car" that specifies the properties (attributes) a car can have, like color, brand, and model, as well as the actions (methods) a car can perform, like driving or honking.

Creating Objects from Classes

Once you have a class defined, you can create multiple objects (instances) based on that class. Each object is like a unique tool in your toolbox, following the blueprint you defined in the class. Each car you create using the "Car" class will have its own specific color, brand, and model, just like how each tool in your toolbox may have different sizes and functions.



Classes and Objects

Let's dive deeper into the concepts of Object-Oriented Programming (OOP) in Python with real-world examples.

Think of a class as a blueprint, and an object as a house built from that blueprint. A class defines the structure and behavior of objects, similar to how a blueprint specifies the layout and features of a house. The class contains attributes (variables) that represent the characteristics of the object and methods (functions) that define what the object can do.

Real-world example:

Consider the Car class. Each car on the road is an object created from the Car class blueprint. The attributes of a car object might include its make, model, year, and color. The methods might include start(), stop(), and accelerate() to control the car's actions.  So attributes hold information. about each car, and methods represent things the car can do. Another way to think about methods is that they are functions that live inside the class, representing things that the class can do.

class Car:
def __init__(self, make, model, year, color):
self.make = make
self.model = model
self.year = year
self.color = color

def start(self):
print(f"The {self.make} {self.model} has started.")

def stop(self):
print(f"The {self.make} {self.model} has stopped.")

def accelerate(self):
print(f"The {self.make} {self.model} is accelerating.")


Attributes and Methods

Let's dig a little deeper into attributes and methods…

Attributes represent the data or properties of an object, and methods represent the actions or behaviors the object can perform. Attributes are like characteristics of an object, and methods are like actions that object can take.

Real-world example:

Imagine a Person class. Each person has attributes such as name, age, and occupation, which describe them. Methods could be speak(), eat(), and work(), representing the actions a person can do.

class Person:
def __init__(self, name, age, occupation):
self.name = name
self.age = age
self.occupation = occupation

def speak(self):
print(f"Hello, my name is {self.name}.")

def eat(self):
print(f"{self.name} is eating.")

def work(self):
print(f"{self.name} is working as a {self.occupation}.")


The Constructor Method

The constructor method is a special method that initializes the attributes of an object when it is created. It is like a factory that sets up the object based on the blueprint (class). In other words, we can create a constructor method so that we can easily set the value of attributes for each object as they are created, very similar to the way we pass arguments to a function when it is called.

Real-world example:

Imagine a Dog class. When you bring home a new puppy, you use the constructor to give it a name and age, and those become its attributes.

class Dog:
def __init__(self, name, age):
self.name = name
self.age = age

def bark(self):
print(f"{self.name} says, 'Woof! Woof!'")

# Creating two objects based on the dog class:
poodle = Dog("Sam", 8)
lab = Dog("Max", 4)


Encapsulation

In OOP, encapsulation means bundling data (attributes) and actions (methods) that belong together within a single object. This helps keep the code organized and prevents accidental changes to the data from outside the object. It's like keeping all the parts of a tool together inside its own toolbox to avoid losing or misusing them.

Real-world example:

Consider a BankAccount class. Encapsulation ensures that other parts of the program cannot directly change the account balance; they can only use methods like deposit() and withdraw() to access it securely.

class BankAccount:
def __init__(self, account_number, balance):
self._account_number = account_number
self._balance = balance

def deposit(self, amount):
self._balance += amount

def withdraw(self, amount):
if amount <= self._balance:
self._balance -= amount
else:
print("Insufficient funds.")

These concepts provide a powerful foundation for building complex applications by organizing data and behavior into meaningful objects. As you progress in your programming journey, you will find OOP to be a crucial skill for solving real-world problems in a systematic and scalable way.



Using a List to Organize Objects

At times, if you are creating many objects from a single class, it is useful to group them together in a single list variable. The following example will walk you through this process, and help you see how to implement it in code.

Let's walk through the process of creating a Fruit class to represent different types of fruits in a fruit stand. Then, we'll use a list variable to organize a group of objects into the fruit stand. We'll also demonstrate how to add and subtract fruits from the inventory using methods in the class.

Step 1: Creating the Fruit Class

The Fruit class will represent individual types of fruits in the fruit stand. Each fruit will have attributes like name, quantity, and price. We'll also create methods to add and subtract fruits from the inventory.

class Fruit:
def __init__(self, name, quantity, price):
self.name = name
self.quantity = quantity
self.price = price

def add_fruit(self, amount):
self.quantity += amount

def remove_fruit(self, amount):
if amount <= self.quantity:
self.quantity -= amount
else:
print(f"Not enough {self.name} in the inventory.")

Step 2: Creating the Fruit Stand

Now, let's create a list variable fruit_stand to organize the group of fruit objects in the fruit stand. We can add different types of fruits as objects to the fruit_stand list.

# Create a list to represent the fruit stand inventory
fruit_stand = []

# Add different types of fruits to the fruit_stand list
fruit_stand.append(Fruit("Apple", 10, 1.50))
fruit_stand.append(Fruit("Banana", 15, 0.75))
fruit_stand.append(Fruit("Orange", 20, 1.25))

Step 3: Interacting with the Fruit Stand

Now that we have set up the Fruit class and created the fruit_stand list, we can interact with the fruit stand by adding and subtracting fruits from the inventory using the methods in the Fruit class.

# Display the current fruit stand inventory
def display_inventory():
print("Fruit Stand Inventory:")
for fruit in fruit_stand:
print(f"{fruit.name} - Quantity: {fruit.quantity}, Price: ${fruit.price:.2f}")

# Add more fruits to the inventory
def add_fruits_to_inventory(fruit_name, amount):
for fruit in fruit_stand:
if fruit.name == fruit_name:
fruit.add_fruit(amount)
print(f"Added {amount} {fruit.name}(s) to the inventory.")
return
print(f"{fruit_name} not found in the fruit stand.")

# Remove fruits from the inventory
def remove_fruits_from_inventory(fruit_name, amount):
for fruit in fruit_stand:
if fruit.name == fruit_name:
fruit.remove_fruit(amount)
print(f"Removed {amount} {fruit.name}(s) from the inventory.")
return
print(f"{fruit_name} not found in the fruit stand.")

# Display the initial inventory
display_inventory()

# Add more fruits to the inventory
add_fruits_to_inventory("Apple", 5)
add_fruits_to_inventory("Grapes", 8)

# Display the updated inventory
display_inventory()

# Remove fruits from the inventory
remove_fruits_from_inventory("Banana", 10)
remove_fruits_from_inventory("Orange", 25)

# Display the final inventory
display_inventory()
 

Here is the resulting output:

Fruit Stand Inventory:
Apple - Quantity: 10, Price: $1.50
Banana - Quantity: 15, Price: $0.75
Orange - Quantity: 20, Price: $1.25
Added 5 Apple(s) to the inventory.
Grapes not found in the fruit stand.
Fruit Stand Inventory:
Apple - Quantity: 15, Price: $1.50
Banana - Quantity: 15, Price: $0.75
Orange - Quantity: 20, Price: $1.25
Removed 10 Banana(s) from the inventory.
Not enough Orange in the inventory.
Fruit Stand Inventory:
Apple - Quantity: 15, Price: $1.50
Banana - Quantity: 5, Price: $0.75
Orange - Quantity: 20, Price: $1.25

In this example, we created a fruit stand inventory using a list variable (fruit_stand) containing different fruit objects. We then interacted with the fruit stand by adding and removing fruits from the inventory using the methods defined in the Fruit class.

By organizing fruit objects into a list, we can easily manage the inventory of the fruit stand and perform various operations on the individual fruit items. The use of a class for fruits allows us to encapsulate the data and behavior related to each fruit, making the code more organized and maintainable.



Aggregation Classes

In object-oriented programming (OOP) with Python, aggregation is a fundamental concept that represents a "whole-part" relationship between classes. It allows one class to contain and manage instances of another class, creating a more complex structure. In aggregation, the "whole" class, known as the container or aggregator, holds references to the "part" classes, which are the objects being aggregated. These part classes can exist independently and may have their own lifecycle, even if the container class is destroyed. Aggregation is a way to model real-world relationships where one object is composed of or contains other objects. In Python, this relationship is established by including instances of one class within another class as attributes, effectively forming a hierarchical or nested structure.

For instance, a Library class can aggregate Book objects as its parts. Each book is a separate object with its own attributes and methods, but they are managed collectively by the library. Aggregation provides a flexible and modular way to structure code, making it easier to manage complex systems by breaking them down into smaller, reusable components. It enhances code readability, maintainability, and reusability, promoting the principles of encapsulation and modular design in OOP.

Here is a code example of how it would work:

class Book:
def __init__(self, title, author):
self.title = title # Initialize the title attribute with the provided title
self.author = author # Initialize the author attribute with the provided author

def __str__(self):
return f"{self.title} by {self.author}" # Define a string representation for the book object

class Library:
def __init__(self):
self.books = [] # Initialize an empty list to hold Book objects (aggregation)

def add_book(self, book):
self.books.append(book) # Add a Book object to the list of books

def remove_book(self, book):
if book in self.books:
self.books.remove(book) # Remove a Book object from the list of books if it exists
else:
print(f"{book} is not in the library.")

def list_books(self):
for i, book in enumerate(self.books, start=1):
print(f"{i}. {book}") # List the books in the library with their index

# Create a Library object
library = Library()

while True:
print("\nLibrary Menu:")
print("1. Add a Book")
print("2. Remove a Book")
print("3. List Books")
print("4. Quit")

choice = input("Enter your choice: ")

if choice == "1":
title = input("Enter the book title: ")
author = input("Enter the author: ")
new_book = Book(title, author) # Create a new Book object
library.add_book(new_book) # Add the new book to the library
print(f"{new_book} has been added to the library.")

elif choice == "2":
print("Books in the Library:")
library.list_books()
book_index = int(input("Enter the number of the book to remove: ")) - 1
if 0 <= book_index < len(library.books):
removed_book = library.books[book_index]
library.remove_book(removed_book) # Remove the selected book from the library
print(f"{removed_book} has been removed from the library.")
else:
print("Invalid book number. No book removed.")

elif choice == "3":
print("Books in the Library:")
library.list_books() # List the books in the library

elif choice == "4":
print("Goodbye!")
break

else:
print("Invalid choice. Please select a valid option.")
 

Videos for Module 14 - Introducing Object-Oriented Programming

Introduction to Object-Oriented Programming (1:43)

Classes and Object-Oriented Programming (2:50)

Classes and Properties (2:28)

Classes and Methods (4:04)

Initialization Methods in OOP (3:53)

Objects in Lists (3:14)

Iterating Through Object Lists (1:15)

Adding Objects to Lists (2:39)

OOP Virtual Pet Demo (4:29)

OOP Virtual Pet Code, Part 1 (6:54)

OOP Virtual Pet Code, Part 2 (16:34)

OOP Quick Review (2:42)

Revisiting the __init__ Method (:56)

The __str__ Method in OOP (4:34)

Getter and Setter Methods (9:19)

A14 Introduction (1:30)

A14 Code (Bad Audio) (14:32)

Key Terms for Module 14 - Introducing Object-Oriented Programming

No terms have been published for this module.

Quiz Yourself - Module 14 - Introducing Object-Oriented Programming

Test your knowledge of this module by choosing options below. You can keep trying until you get the right answer.

Skip to the Next Question