Object-Oriented Programming organizes code around 'objects' that represent real-world things. A class is a blueprint that defines attributes (data) and methods (functions) for objects. Four main concepts: 1) Encapsulation (bundling data and methods), 2) Inheritance (reusing code), 3) Polymorphism (same interface, different behavior), 4) Abstraction (hiding complexity).
# Object-Oriented Programming - Part 1
print("OBJECT-ORIENTED PROGRAMMING (OOP) - PART 1")
print("=" * 60)
# Example 1: Creating a simple class
print("\n1. CREATING A SIMPLE CLASS")
print("-" * 30)
class Dog:
"""A simple Dog class"""
# Constructor method (initializer)
def __init__(self, name, age, breed):
"""Initialize dog attributes"""
self.name = name # Instance variable
self.age = age # Instance variable
self.breed = breed # Instance variable
# Instance method
def bark(self):
"""Make the dog bark"""
return f"{self.name} says: Woof!"
def get_info(self):
"""Get dog information"""
return f"{self.name} is a {self.age}-year-old {self.breed}"
def have_birthday(self):
"""Celebrate dog's birthday"""
self.age += 1
return f"Happy Birthday {self.name}! Now {self.age} years old."
# Creating objects (instances) from the class
print("Creating dog objects:")
dog1 = Dog("Buddy", 3, "Golden Retriever")
dog2 = Dog("Luna", 2, "German Shepherd")
print(f"Dog 1: {dog1.get_info()}")
print(f"Dog 2: {dog2.get_info()}")
print(f"\n{dog1.bark()}")
print(f"{dog2.bark()}")
# Calling methods
print(f"\n{dog1.have_birthday()}")
print(f"Updated: {dog1.get_info()}")
# Accessing attributes directly
print(f"\nDirect attribute access:")
print(f"Dog1's name: {dog1.name}")
print(f"Dog2's breed: {dog2.breed}")
# Example 2: Bank Account class
print("\n\n2. BANK ACCOUNT CLASS")
print("-" * 30)
class BankAccount:
"""A simple bank account class"""
# Class variable (shared by all instances)
bank_name = "Python Bank"
def __init__(self, account_holder, initial_balance=0):
self.account_holder = account_holder
self.balance = initial_balance
self.account_number = self._generate_account_number()
def _generate_account_number(self):
"""Private method to generate account number"""
import random
return f"ACC{random.randint(10000, 99999)}"
def deposit(self, amount):
"""Deposit money into account"""
if amount > 0:
self.balance += amount
return f"Deposited ${amount:.2f}. New balance: ${self.balance:.2f}"
return "Deposit amount must be positive"
def withdraw(self, amount):
"""Withdraw money from account"""
if amount > self.balance:
return "Insufficient funds!"
elif amount <= 0:
return "Withdrawal amount must be positive"
else:
self.balance -= amount
return f"Withdrew ${amount:.2f}. New balance: ${self.balance:.2f}"
def check_balance(self):
"""Check current balance"""
return f"Account balance: ${self.balance:.2f}"
def transfer(self, amount, other_account):
"""Transfer money to another account"""
if amount > self.balance:
return "Transfer failed: Insufficient funds"
# Withdraw from this account
self.balance -= amount
# Deposit to other account
other_account.balance += amount
return f"Transferred ${amount:.2f} to {other_account.account_holder}"
def get_statement(self):
"""Get account statement"""
return f"""
{self.bank_name}
Account Holder: {self.account_holder}
Account Number: {self.account_number}
Balance: ${self.balance:.2f}
"""
# Using the BankAccount class
print(f"Bank: {BankAccount.bank_name}")
# Create accounts
account1 = BankAccount("Alice", 1000)
account2 = BankAccount("Bob", 500)
print(f"\nCreated accounts:")
print(f"Alice's account: {account1.account_number}")
print(f"Bob's account: {account2.account_number}")
# Perform transactions
print("\nTransactions:")
print(account1.deposit(200))
print(account1.withdraw(150))
print(account1.withdraw(2000)) # Should fail
# Transfer money
print(f"\nBefore transfer:")
print(f"Alice: {account1.check_balance()}")
print(f"Bob: {account2.check_balance()}")
print(f"\n{account1.transfer(300, account2)}")
print(f"\nAfter transfer:")
print(f"Alice: {account1.check_balance()}")
print(f"Bob: {account2.check_balance()}")
# Get statement
print("\nAccount statement:")
print(account1.get_statement())
# Example 3: Class vs Instance variables
print("\n\n3. CLASS VS INSTANCE VARIABLES")
print("-" * 30)
class Student:
# Class variable (shared by all students)
school_name = "Python High School"
total_students = 0
def __init__(self, name, grade):
# Instance variables (unique to each student)
self.name = name
self.grade = grade
Student.total_students += 1 # Modify class variable
self.student_id = f"S{Student.total_students:03d}"
@classmethod
def get_school_info(cls):
"""Class method - works on class, not instance"""
return f"School: {cls.school_name}, Total Students: {cls.total_students}"
@staticmethod
def calculate_average(grades):
"""Static method - doesn't need class or instance"""
if not grades:
return 0
return sum(grades) / len(grades)
def get_info(self):
return f"{self.name} (ID: {self.student_id}), Grade: {self.grade}"
# Create students
student1 = Student("Alice", 10)
student2 = Student("Bob", 11)
student3 = Student("Charlie", 12)
print("Student Information:")
print(f"School: {Student.school_name}")
print(f"Total Students: {Student.total_students}")
print(f"\nIndividual students:")
print(student1.get_info())
print(student2.get_info())
print(student3.get_info())
# Using class method
print(f"\n{Student.get_school_info()}")
# Using static method
grades = [85, 90, 78, 92, 88]
print(f"\nAverage of grades {grades}: {Student.calculate_average(grades):.1f}")
# Example 4: Property decorators (getters/setters)
print("\n\n4. PROPERTY DECORATORS")
print("-" * 30)
class Temperature:
def __init__(self, celsius):
self._celsius = celsius # Private variable convention
@property
def celsius(self):
"""Getter for celsius"""
return self._celsius
@celsius.setter
def celsius(self, value):
"""Setter for celsius with validation"""
if value < -273.15:
raise ValueError("Temperature cannot be below absolute zero!")
self._celsius = value
@property
def fahrenheit(self):
"""Property that calculates Fahrenheit"""
return (self._celsius * 9/5) + 32
@fahrenheit.setter
def fahrenheit(self, value):
"""Setter for Fahrenheit"""
self._celsius = (value - 32) * 5/9
# Using properties
temp = Temperature(25)
print(f"Initial: {temp.celsius}°C = {temp.fahrenheit:.1f}°F")
# Change celsius
print("\nChanging celsius to 30:")
temp.celsius = 30
print(f"Now: {temp.celsius}°C = {temp.fahrenheit:.1f}°F")
# Change fahrenheit
print("\nSetting fahrenheit to 100:")
temp.fahrenheit = 100
print(f"Now: {temp.celsius:.1f}°C = {temp.fahrenheit}°F")
# Try invalid temperature
try:
print("\nTrying to set invalid temperature:")
temp.celsius = -300 # Should raise error
except ValueError as e:
print(f"Error: {e}")
# Example 5: Simple OOP project - Library System
print("\n\n5. LIBRARY SYSTEM PROJECT")
print("-" * 30)
class Book:
def __init__(self, title, author, isbn):
self.title = title
self.author = author
self.isbn = isbn
self.is_checked_out = False
self.checked_out_by = None
def check_out(self, member_name):
if not self.is_checked_out:
self.is_checked_out = True
self.checked_out_by = member_name
return f"'{self.title}' checked out by {member_name}"
return f"'{self.title}' is already checked out by {self.checked_out_by}"
def return_book(self):
if self.is_checked_out:
member = self.checked_out_by
self.is_checked_out = False
self.checked_out_by = None
return f"'{self.title}' returned by {member}"
return f"'{self.title}' was not checked out"
def __str__(self):
status = "Checked out" if self.is_checked_out else "Available"
if self.is_checked_out:
status += f" by {self.checked_out_by}"
return f"'{self.title}' by {self.author} - {status}"
class Library:
def __init__(self, name):
self.name = name
self.books = []
def add_book(self, book):
self.books.append(book)
print(f"Added: {book.title}")
def find_book(self, title):
for book in self.books:
if book.title.lower() == title.lower():
return book
return None
def list_books(self):
if not self.books:
print("No books in library")
else:
print(f"\nBooks in {self.name}:")
for book in self.books:
print(f" - {book}")
# Create library and books
library = Library("City Library")
book1 = Book("Python Programming", "John Doe", "123456")
book2 = Book("Data Science Basics", "Jane Smith", "789012")
book3 = Book("Web Development", "Bob Johnson", "345678")
library.add_book(book1)
library.add_book(book2)
library.add_book(book3)
# List all books
library.list_books()
# Check out and return books
print("\nChecking out books:")
print(book1.check_out("Alice"))
print(book2.check_out("Bob"))
print(book1.check_out("Charlie")) # Should fail
library.list_books()
print("\nReturning books:")
print(book1.return_book())
library.list_books()