Table of contents

Object-Oriented Programming (OOP)

Object-Oriented Programming (OOP) is a programming paradigm that uses objects and classes to structure code. This guide will cover the fundamentals of OOP in Python, including classes, objects, inheritance, polymorphism, encapsulation, and more. We’ll also provide detailed examples to help you understand and apply these concepts.

A class is a blueprint for creating objects. It defines a set of attributes and methods that the created objects will have.

An object is an instance of a class. It encapsulates data and functions that operate on the data.

Example: Defining a Class and Creating an Object

class Dog: # Class attribute species = "Canis familiaris" # Initializer / Instance attributes def __init__(self, name, age): self.name = name self.age = age # Instance method def description(self): return f"{self.name} is {self.age} years old" # Another instance method def speak(self, sound): return f"{self.name} says {sound}" # Creating an object (instance) of the Dog class my_dog = Dog("Buddy", 3) print(my_dog.description()) # Output: Buddy is 3 years old print(my_dog.speak("Woof Woof")) # Output: Buddy says Woof Woof

Instance attributes are variables that are unique to each instance of a class. Methods are functions defined inside a class that operate on the instance attributes.

Example

class Cat: def __init__(self, name, age): self.name = name self.age = age def description(self): return f"{self.name} is {self.age} years old" my_cat = Cat("Whiskers", 5) print(my_cat.description()) # Output: Whiskers is 5 years old

Class attributes are variables that are shared among all instances of a class. Class methods are methods that operate on the class itself rather than on the instance.

Example

class Bird: # Class attribute species = "Aves" def __init__(self, name, age): self.name = name self.age = age # Instance method def description(self): return f"{self.name} is {self.age} years old" # Class method @classmethod def common_species(cls): return f"All birds are {cls.species}" my_bird = Bird("Tweety", 2) print(my_bird.description()) # Output: Tweety is 2 years old print(Bird.common_species()) # Output: All birds are Aves

Inheritance allows a class to inherit attributes and methods from another class, promoting code reuse.

Example

class Animal: def __init__(self, name): self.name = name def eat(self): return f"{self.name} is eating" # Inherit from Animal class class Dog(Animal): def bark(self): return f"{self.name} is barking" my_dog = Dog("Buddy") print(my_dog.eat()) # Output: Buddy is eating print(my_dog.bark()) # Output: Buddy is barking

Polymorphism allows methods to be used interchangeably, regardless of the class they belong to, as long as they provide the same interface.

Example

class Cat: def speak(self): return "Meow" class Dog: def speak(self): return "Woof" def make_animal_speak(animal): print(animal.speak()) my_cat = Cat() my_dog = Dog() make_animal_speak(my_cat) # Output: Meow make_animal_speak(my_dog) # Output: Woof

Encapsulation restricts access to certain attributes and methods, preventing direct modification. This is achieved using private attributes and methods.

Example

class BankAccount: def __init__(self, balance): self.__balance = balance # Private attribute def deposit(self, amount): if amount > 0: self.__balance += amount def withdraw(self, amount): if 0 < amount <= self.__balance: self.__balance -= amount def get_balance(self): return self.__balance my_account = BankAccount(100) my_account.deposit(50) my_account.withdraw(30) print(my_account.get_balance()) # Output: 120

Defining the Book Class

class Book: def __init__(self, title, author, year): self.title = title self.author = author self.year = year def description(self): return f"'{self.title}' by {self.author} ({self.year})"

Defining the Library Class

class Library: def __init__(self): self.books = [] def add_book(self, book): self.books.append(book) def remove_book(self, title): self.books = [book for book in self.books if book.title != title] def list_books(self): for book in self.books: print(book.description()) # Creating books book1 = Book("To Kill a Mockingbird", "Harper Lee", 1960) book2 = Book("1984", "George Orwell", 1949) book3 = Book("The Great Gatsby", "F. Scott Fitzgerald", 1925) # Creating a library and adding books my_library = Library() my_library.add_book(book1) my_library.add_book(book2) my_library.add_book(book3) # Listing books in the library my_library.list_books() # Output: # 'To Kill a Mockingbird' by Harper Lee (1960) # '1984' by George Orwell (1949) # 'The Great Gatsby' by F. Scott Fitzgerald (1925) # Removing a book and listing books again my_library.remove_book("1984") my_library.list_books() # Output: # 'To Kill a Mockingbird' by Harper Lee (1960) # 'The Great Gatsby' by F. Scott Fitzgerald (1925)

Create a Student class with attributes for name, age, and grade. Implement methods to display student information and to update the grade. Then, create a School class to manage multiple students.

Example

class Student: def __init__(self, name, age, grade): self.name = name self.age = age self.grade = grade def display_info(self): return f"{self.name}, Age: {self.age}, Grade: {self.grade}" def update_grade(self, new_grade): self.grade = new_grade class School: def __init__(self): self.students = [] def add_student(self, student): self.students.append(student) def remove_student(self, name): self.students = [student for student in self.students if student.name != name] def list_students(self): for student in self.students: print(student.display_info()) # Creating students student1 = Student("Alice", 14, "8th") student2 = Student("Bob", 15, "9th") # Creating a school and adding students my_school = School() my_school.add_student(student1) my_school.add_student(student2) # Listing students in the school my_school.list_students() # Output: # Alice, Age: 14, Grade: 8th # Bob, Age: 15, Grade: 9th # Updating a student's grade and listing students again student1.update_grade("9th") my_school.list_students() # Output: # Alice, Age: 14, Grade: 9th # Bob, Age: 15, Grade: 9th

Create a BankAccount class with methods to deposit, withdraw, and check the balance. Then, create a Bank class to manage multiple accounts.

Example

class BankAccount: def __init__(self, account_number, balance=0): self.account_number = account_number self.__balance = balance def deposit(self, amount): if amount > 0: self.__balance += amount def withdraw(self, amount): if 0 < amount <= self.__balance: self.__balance -= amount def get_balance(self): return self.__balance def display_info(self): return f"Account {self.account_number}, Balance: {self.__balance}" class Bank: def __init__(self): self.accounts = [] def add_account(self, account): self.accounts.append(account) def remove_account(self, account_number): self.accounts = [acc for acc in self.accounts if acc.account_number != account_number] def list_accounts(self): for account in self.accounts: print(account.display_info()) # Creating accounts account1 = BankAccount("001", 1000) account2 = BankAccount(“002”, 500) # Creating a bank and adding accounts my_bank = Bank() my_bank.add_account(account1) my_bank.add_account(account2) # Listing accounts in the bank my_bank.list_accounts() # Output # Account 001, Balance: 1500 # Account 002, Balance: 500

In this guide, we've covered the fundamentals of Object-Oriented Programming (OOP) in Python, including classes, objects, instance and class attributes, methods, inheritance, polymorphism, and encapsulation. We've also provided detailed examples and exercises to help you understand and apply these concepts. OOP is a powerful paradigm that promotes code reuse, modularity, and maintainability. Practice these concepts with the provided examples and exercises to enhance your understanding and programming skills. In the next section, we will delve into error handling and exceptions in Python, which are crucial for writing robust and error-free code. Happy coding!