OOP Practice Questions
Python Object-Oriented Programming (OOP) Practice Companion
This workbook contains 50 progressive, high-quality programming exercises categorized into 5 distinct levels of difficulty. Each question represents an operational, real-world data processing scenario solved using a strictly object-oriented paradigm.
Every single solution is complete and executable, showcasing standard object-oriented patterns in Python (including class definitions, properties, encapsulation, inheritance, multiple inheritance, Method Resolution Order, polymorphism, duck typing, and magic methods). There are zero database engines (no SQLite) and zero external packages (no Pandas) to ensure absolute compliance with the Phase 1 Python Foundations syllabus.
Table of Contents
Level 1: Object Creation, Attributes, and Basic Methods
Question 1: Basic Class Definition
Define a class Book that represents a catalog entry. It must contain the instance attributes title, author, and pages.
main.pyRunnableclass Book: """Represents a basic catalog book record""" def __init__(self, title, author, pages): self.title = title self.author = author self.pages = pages ## Verification book = Book("AP Calculus", "Dr. Miller", 450) print(f"Title: {book.title} | Author: {book.author} | Pages: {book.pages}")
Expected Output:
Title: AP Calculus | Author: Dr. Miller | Pages: 450
Question 2: Multiple Instances Isolation
Instantiate two separate objects of the Book class and verify that their attributes reside in distinct memory registers.
main.pyRunnable## Instantiate two unique objects book_a = Book("Biology 101", "Dr. Green", 320) book_b = Book("Chemistry 101", "Dr. Boyle", 280) print(f"Book A: {book_a.title} ({book_a.pages} pages)") print(f"Book B: {book_b.title} ({book_b.pages} pages)") print(f"Are instances identical? {book_a is book_b}")
Expected Output:
Book A: Biology 101 (320 pages) Book B: Chemistry 101 (280 pages) Are instances identical? False
Question 3: Basic Instance Method
Extend the Book class by adding an instance method is_long(self) that returns True if the book contains more than 300 pages, and False otherwise.
main.pyRunnableclass Book: def __init__(self, title, author, pages): self.title = title self.author = author self.pages = pages def is_long(self): """Returns True if pages exceed 300""" return self.pages > 300 ## Verification book_a = Book("Intro to Physics", "Dr. Newton", 400) book_b = Book("Short Stories", "Various", 150) print(f"{book_a.title} is long: {book_a.is_long()}") print(f"{book_b.title} is long: {book_b.is_long()}")
Expected Output:
Intro to Physics is long: True Short Stories is long: False
Question 4: Class with Default Parameters
Define a class AcademicRectangle with instance attributes width and height. Provide default parameter values of 1.0 in the constructor.
main.pyRunnableclass AcademicRectangle: """Represents a geometric classroom desk space""" def __init__(self, width=1.0, height=1.0): self.width = width self.height = height ## Verification default_rect = AcademicRectangle() custom_rect = AcademicRectangle(4.5, 2.0) print(f"Default: {default_rect.width} x {default_rect.height}") print(f"Custom: {custom_rect.width} x {custom_rect.height}")
Expected Output:
Default: 1.0 x 1.0 Custom: 4.5 x 2.0
Question 5: State Modification via Methods
Define a class BankAccount containing the attributes owner and balance. Add the instance methods deposit(amount) and withdraw(amount) to modify the internal state.
main.pyRunnableclass BankAccount: """Represents an active banking account record""" def __init__(self, owner, initial_balance=0.0): self.owner = owner self.balance = float(initial_balance) def deposit(self, amount): self.balance += float(amount) return self.balance def withdraw(self, amount): if amount > self.balance: print("[Warning] Insufficient funds.") return False self.balance -= float(amount) return self.balance ## Verification account = BankAccount("Alice", 100.0) account.deposit(50.0) print(f"Balance after deposit: {account.balance}") account.withdraw(30.0) print(f"Balance after withdrawal: {account.balance}")
Expected Output:
Balance after deposit: 150.0 Balance after withdrawal: 120.0
Question 6: Storing Collections as Attributes
Define a class AcademicPlaylist containing instance attributes name (string) and tracks (list). Add methods add_track(track_name) and remove_track(track_name).
main.pyRunnableclass AcademicPlaylist: """Manages collections of instructional tracks""" def __init__(self, name): self.name = name self.tracks = [] def add_track(self, track_name): self.tracks.append(track_name) def remove_track(self, track_name): if track_name in self.tracks: self.tracks.remove(track_name) ## Verification playlist = AcademicPlaylist("Python Lectures") playlist.add_track("Lecture 1: Basics") playlist.add_track("Lecture 2: OOP") print(f"Tracks: {playlist.tracks}") playlist.remove_track("Lecture 1: Basics") print(f"Remaining Tracks: {playlist.tracks}")
Expected Output:
Tracks: ['Lecture 1: Basics', 'Lecture 2: OOP'] Remaining Tracks: ['Lecture 2: OOP']
Question 7: Simple State Toggle
Define a class SmartLight with instances containing a boolean state is_on. Add methods turn_on(), turn_off(), and toggle().
main.pyRunnableclass SmartLight: """Controls school campus lighting hardware status""" def __init__(self): self.is_on = False def turn_on(self): self.is_on = True def turn_off(self): self.is_on = False def toggle(self): self.is_on = not self.is_on ## Verification light = SmartLight() print(f"Initial: {light.is_on}") light.turn_on() print(f"After Turn On: {light.is_on}") light.toggle() print(f"After Toggle: {light.is_on}")
Expected Output:
Initial: False After Turn On: True After Toggle: False
Question 8: Class Variables vs. Instance Variables
Define a class DistrictEmployee containing an instance variable name and a shared class variable school_name.
main.pyRunnableclass DistrictEmployee: # Shared Class Variable school_name = "Lincoln High School" def __init__(self, name): # Unique Instance Variable self.name = name ## Verification emp_a = DistrictEmployee("Alice") emp_b = DistrictEmployee("Bob") print(f"Employee A: {emp_a.name} | School: {emp_a.school_name}") print(f"Employee B: {emp_b.name} | School: {emp_b.school_name}") ## Change class variable globally DistrictEmployee.school_name = "Grandview Academy" print(f"Updated School: {emp_a.school_name} | {emp_b.school_name}")
Expected Output:
Employee A: Alice | School: Lincoln High School Employee B: Bob | School: Lincoln High School Updated School: Grandview Academy | Grandview Academy
Question 9: Overriding str Method
Define a class RegisteredStudent with attributes name and grade_level. Override the magic __str__ method to return a user-friendly profile description.
main.pyRunnableclass RegisteredStudent: def __init__(self, name, grade_level): self.name = name self.grade_level = grade_level def __str__(self): """User-friendly representation""" return f"Registered Student '{self.name}' in grade {self.grade_level}" ## Verification student = RegisteredStudent("Alice Johnson", 11) print(student)
Expected Output:
Registered Student 'Alice Johnson' in grade 11
Question 10: Type Inspections
Write a script that creates an instance of RegisteredStudent and inspects its class relationships using type() and isinstance().
main.pyRunnablestudent = RegisteredStudent("Bob Smith", 12) print(f"Instance class type: {type(student)}") print(f"Is instance of RegisteredStudent? {isinstance(student, RegisteredStudent)}") print(f"Is instance of object base class? {isinstance(student, object)}")
Expected Output:
Instance class type: <class '__main__.RegisteredStudent'> Is instance of RegisteredStudent? True Is instance of object base class? True
Level 1 Checkpoint
Level 2: Constructors, State Validation, and Properties
Question 11: Constructor Type Validation
Define a class CourseEnrollment that validates that the student_name argument is a non-empty string, raising a TypeError if validation fails.
main.pyRunnableclass CourseEnrollment: def __init__(self, student_name): if not isinstance(student_name, str) or not student_name.strip(): raise TypeError("student_name must be a non-empty string.") self.student_name = student_name.strip() ## Verification try: invalid = CourseEnrollment(12345) except TypeError as error: print(f"Error caught: {error}")
Expected Output:
Error caught: student_name must be a non-empty string.
Question 12: Constructor Value Validation
Define a class InventoryItem with attributes name and price. Validate that price is a non-negative number, raising a ValueError if the value is invalid.
main.pyRunnableclass InventoryItem: def __init__(self, name, price): self.name = name if not isinstance(price, (int, float)) or price < 0.0: raise ValueError("Price must be a non-negative number.") self.price = float(price) ## Verification try: item = InventoryItem("Calculus Textbook", -15.50) except ValueError as error: print(f"Error caught: {error}")
Expected Output:
Error caught: Price must be a non-negative number.
Question 13: Property Getter & Setter
Define a class TemperatureSensor containing a private attribute __celsius. Provide property getters and setters for the fahrenheit temperature scale.
main.pyRunnableclass TemperatureSensor: def __init__(self, celsius=0.0): self.__celsius = float(celsius) @property def fahrenheit(self): """Getter for fahrenheit conversion""" return (self.__celsius * 9/5) + 32 @fahrenheit.setter def fahrenheit(self, value): """Setter accepting fahrenheit and converting internally to celsius""" self.__celsius = (float(value) - 32) * 5/9 ## Verification sensor = TemperatureSensor(25.0) # 25C = 77F print(f"Temperature (F): {sensor.fahrenheit:.2f}") sensor.fahrenheit = 95.0 # 95F = 35C print(f"Converted internally, Celsius: {sensor._TemperatureSensor__celsius:.2f}")
Expected Output:
Temperature (F): 77.00 Converted internally, Celsius: 35.00
Question 14: Encapsulated Property Setter
Define a class Product with an encapsulated price property. Prevent assignments of negative prices by raising a ValueError.
main.pyRunnableclass Product: def __init__(self, name, price): self.name = name self.price = price # Invokes setter property @property def price(self): return self.__price @price.setter def price(self, value): if not isinstance(value, (int, float)) or value < 0.0: raise ValueError("Price must be a non-negative number.") self.__price = float(value) ## Verification prod = Product("Desk Chair", 49.99) print(f"Price: {prod.price}") try: prod.price = -5.00 except ValueError as error: print(f"Error caught: {error}")
Expected Output:
Price: 49.99 Error caught: Price must be a non-negative number.
Question 15: Class Method Constructor Factory
Define a class DistrictCourse with attributes subject and code. Provide a @classmethod factory named from_string(cls, data_string) that parses a comma-separated string (e.g., "Physics,PHYS-101") to initialize the object.
main.pyRunnableclass DistrictCourse: def __init__(self, subject, code): self.subject = subject self.code = code @classmethod def from_string(cls, data_string): """Factory method to parse a string input""" parts = data_string.split(",") if len(parts) != 2: raise ValueError("Invalid format. Expected 'Subject,Code'") return cls(parts[0].strip(), parts[1].strip()) ## Verification course = DistrictCourse.from_string("Chemistry,CHEM-201") print(f"Subject: {course.subject} | Code: {course.code}")
Expected Output:
Subject: Chemistry | Code: CHEM-201
Question 16: Static Utility Method
Define a class ConfigValidator containing a @staticmethod named is_valid_domain(email_str) that verifies whether a string contains a valid school domain extension (@school.edu).
main.pyRunnableclass ConfigValidator: @staticmethod def is_valid_domain(email_str): """Static method containing no self or cls dependencies""" return isinstance(email_str, str) and email_str.endswith("@school.edu") ## Verification print(f"alice@school.edu valid? {ConfigValidator.is_valid_domain('alice@school.edu')}") print(f"bob@gmail.com valid? {ConfigValidator.is_valid_domain('bob@gmail.com')}")
Expected Output:
alice@school.edu valid? True bob@gmail.com valid? False
Question 17: Dynamic Attribute Operations
Use built-in functions hasattr(), getattr(), and setattr() to inspect and modify instance variables of an object dynamically.
main.pyRunnableclass CampusLocker: def __init__(self, locker_id): self.locker_id = locker_id locker = CampusLocker("L-204") ## Verification print(f"Has status? {hasattr(locker, 'status')}") setattr(locker, "status", "active") print(f"Has status now? {hasattr(locker, 'status')}") print(f"Status value: {getattr(locker, 'status')}")
Expected Output:
Has status? False Has status now? True Status value: active
Question 18: Simulated Configuration Loader
Define a class ConfigLoader that sets up configuration values, ensuring that critical parameters (like database hostname) are read-only after setup.
main.pyRunnableclass ConfigLoader: def __init__(self, db_host): self.__db_host = db_host @property def db_host(self): """Read-only property (no setter defined)""" return self.__db_host ## Verification config = ConfigLoader("127.0.0.1") print(f"Host: {config.db_host}") try: config.db_host = "192.168.1.1" except AttributeError as error: print(f"Attribute mutation blocked: {error}")
Expected Output:
Host: 127.0.0.1 Attribute mutation blocked: can't set attribute
Question 19: Object Composition
Implement object composition by defining an Engine class with attribute horsepower, and a Car class that instantiates and stores an Engine object internally.
main.pyRunnableclass Engine: def __init__(self, horsepower): self.horsepower = horsepower class Car: def __init__(self, model, horsepower): self.model = model # Object Composition: Embedding an Engine object inside a Car object self.engine = Engine(horsepower) ## Verification my_car = Car("Model-S", 450) print(f"Car: {my_car.model} | HP: {my_car.engine.horsepower}")
Expected Output:
Car: Model-S | HP: 450
Question 20: Deep Copy vs. Shallow Copy
Show the difference between a shallow copy and a deep copy of a composite class structure using Python's built-in copy module.
main.pyRunnableimport copy class Task: def __init__(self, title): self.title = title class TaskList: def __init__(self, owner): self.owner = owner self.tasks = [] ## Instantiate original original = TaskList("Alice") original.tasks.append(Task("Grades Homework")) ## Perform shallow copy and deep copy shallow = copy.copy(original) deep = copy.deepcopy(original) ## Modify nested object on the shallow copy shallow.tasks[0].title = "Updated Task" print(f"Original Task: {original.tasks[0].title}") print(f"Shallow Copy Task: {shallow.tasks[0].title}") print(f"Deep Copy Task: {deep.tasks[0].title}")
Expected Output:
Original Task: Updated Task Shallow Copy Task: Updated Task Deep Copy Task: Grades Homework
Level 2 Checkpoint
Level 3: Encapsulation & Data Hiding
Question 21: Protected Attribute Convention
Define a class DatabaseConnection containing a protected attribute _connection_string. Demonstrate that this attribute is accessible but intended for internal use.
main.pyRunnableclass DatabaseConnection: def __init__(self, host): # Protected variable prefix convention (_) self._connection_string = f"db://{host}:5432/main" ## Verification conn = DatabaseConnection("localhost") print(f"Accessing protected attribute: {conn._connection_string}")
Expected Output:
Accessing protected attribute: db://localhost:5432/main
Question 22: Private Attributes & Name Mangling
Define a class SecureAccount containing a private attribute __passcode. Attempt direct access and catch the AttributeError, then access the attribute using its mangled name.
main.pyRunnableclass SecureAccount: def __init__(self, passcode): # Private variable prefix convention (__) self.__passcode = passcode ## Verification acc = SecureAccount("secret123") try: print(acc.__passcode) except AttributeError as error: print(f"Direct access blocked: {error}") ## Name Mangling access mangled = acc._SecureAccount__passcode print(f"Mangled access: {mangled}")
Expected Output:
Direct access blocked: 'SecureAccount' object has no attribute '__passcode' Mangled access: secret123
Question 23: Encapsulated Combination Lock
Define a class CombinationLock with a private combination. Provide a public method unlock(attempt) that returns True only if the attempt matches the private combination.
main.pyRunnableclass CombinationLock: def __init__(self, combination): self.__combination = combination def unlock(self, attempt): """Verifies attempt against private combination""" return attempt == self.__combination ## Verification lock = CombinationLock("44-22-11") print(f"Unlock wrong: {lock.unlock('00-00-00')}") print(f"Unlock right: {lock.unlock('44-22-11')}")
Expected Output:
Unlock wrong: False Unlock right: True
Question 24: Read-Only Backing Property
Define a class APIClient containing a private token __token that is set once during instantiation and can only be accessed externally via a read-only @property getter.
main.pyRunnableclass APIClient: def __init__(self, token): self.__token = token @property def token(self): """Read-only property exposing private variable""" return self.__token ## Verification client = APIClient("xyz-token-abc") print(f"API Token: {client.token}") try: client.token = "new-token" except AttributeError as error: print(f"Mutation failed: {error}")
Expected Output:
API Token: xyz-token-abc Mutation failed: can't set attribute
Question 25: Restricting Dynamic Attributes Using slots
Define a class FixedStudent that restricts instance attributes strictly to name and age using __slots__. Verify that attempting to dynamically add a new attribute raises an error.
main.pyRunnableclass FixedStudent: # Restrict instance attribute creation to list elements __slots__ = ["name", "age"] def __init__(self, name, age): self.name = name self.age = age ## Verification student = FixedStudent("Alice", 16) try: student.gpa = 3.8 # Attempting to add an attribute not defined in slots except AttributeError as error: print(f"Dynamic attribute blocked: {error}")
Expected Output:
Dynamic attribute blocked: 'FixedStudent' object has no attribute 'gpa'
Question 26: Class Variable Mutation Protection
Define a class AcademicConstants with a class variable PASSING_THRESHOLD = 60.0. Provide a class method that allows changing the value only if the new value is between 50.0 and 70.0.
main.pyRunnableclass AcademicConstants: __PASSING_THRESHOLD = 60.0 # Private class variable @classmethod def get_threshold(cls): return cls.__PASSING_THRESHOLD @classmethod def update_threshold(cls, new_val): if not (50.0 <= new_val <= 70.0): raise ValueError("Threshold must be between 50.0 and 70.0.") cls.__PASSING_THRESHOLD = float(new_val) ## Verification print(f"Current passing threshold: {AcademicConstants.get_threshold()}%") AcademicConstants.update_threshold(65.0) print(f"Updated passing threshold: {AcademicConstants.get_threshold()}%") try: AcademicConstants.update_threshold(90.0) except ValueError as error: print(f"Invalid update blocked: {error}")
Expected Output:
Current passing threshold: 60.0% Updated passing threshold: 65.0% Invalid update blocked: Threshold must be between 50.0 and 70.0.
Question 27: Property Deleters
Define a class Session with an attribute active_token. Use property decorators to define a getter, setter, and deleter. The deleter must clear the token and print a logout message.
main.pyRunnableclass Session: def __init__(self, initial_token): self.__active_token = initial_token @property def active_token(self): return self.__active_token @active_token.setter def active_token(self, value): self.__active_token = value @active_token.deleter def active_token(self): """Executes custom cleanup when del is called""" print(f"[Log] Purging token: {self.__active_token}") self.__active_token = None ## Verification sess = Session("token-12345") print(f"Token: {sess.active_token}") del sess.active_token print(f"After deletion: {sess.active_token}")
Expected Output:
Token: token-12345 [Log] Purging token: token-12345 After deletion: None
Question 28: Secure Payment Transaction
Define a class SecureTransaction that hides sensitive credit card details (card number, CVV) inside private attributes, exposing only a masked card number format (e.g., ****-****-****-1234).
main.pyRunnableclass SecureTransaction: def __init__(self, card_number, cvv, amount): self.__card_number = card_number self.__cvv = cvv self.amount = float(amount) @property def masked_card(self): """Exposes only the last 4 digits of the card number""" if len(self.__card_number) >= 4: return f"****-****-****-{self.__card_number[-4:]}" return "Invalid Card Format" ## Verification tx = SecureTransaction("1234567812349999", "123", 250.0) print(f"Masked Card: {tx.masked_card} | Amount: ${tx.amount}")
Expected Output:
Masked Card: ****-****-****-9999 | Amount: $250.0
Question 29: Private Utility Methods
Define a class TransactionLogger with a public method log_action(action) and a private utility method __format_log(action) that adds a timestamp to the log. The public method must call the private method internally.
main.pyRunnableimport time class TransactionLogger: def __init__(self): self.logs = [] def __format_log(self, action): """Private utility method""" return f"[AUDIT - 2026] {action}" def log_action(self, action): """Public method calling the private utility method internally""" formatted = self.__format_log(action) self.logs.append(formatted) ## Verification logger = TransactionLogger() logger.log_action("User S101 Login success") print(logger.logs[0])
Expected Output:
[AUDIT - 2026] User S101 Login success
Question 30: Multi-Field Validation Pipeline
Define a class UserProfile containing the private attributes __username, __age, and __email. Implement setter properties for all three attributes, enforcing validation rules (username length, positive age, and email domain).
main.pyRunnableclass UserProfile: def __init__(self, username, age, email): self.username = username self.age = age self.email = email @property def username(self): return self.__username @username.setter def username(self, value): if not isinstance(value, str) or len(value) < 4: raise ValueError("Username must be at least 4 characters long.") self.__username = value @property def age(self): return self.__age @age.setter def age(self, value): if not isinstance(value, int) or value < 0: raise ValueError("Age must be a positive integer.") self.__age = value @property def email(self): return self.__email @email.setter def email(self, value): if not isinstance(value, str) or "@" not in value: raise ValueError("Invalid email address format.") self.__email = value ## Verification try: user = UserProfile("bob", -5, "invalid-email") except ValueError as error: print(f"Error caught: {error}") user = UserProfile("alice_dev", 25, "alice@school.edu") print(f"Profile: {user.username} | Age: {user.age} | Email: {user.email}")
Expected Output:
Error caught: Username must be at least 4 characters long. Profile: alice_dev | Age: 25 | Email: alice@school.edu
Level 3 Checkpoint
Level 4: Inheritance & Polymorphism
Question 31: Basic Inheritance
Define a base class DistrictMember with an attribute name, and a child class StudentMember that inherits from DistrictMember and adds a grade level attribute.
main.pyRunnableclass DistrictMember: """Base parent class""" def __init__(self, name): self.name = name class StudentMember(DistrictMember): """Derived child class""" def __init__(self, name, grade_level): # Initialize the base class attribute super().__init__(name) self.grade_level = grade_level ## Verification student = StudentMember("Alice Johnson", 11) print(f"Name: {student.name} | Grade: {student.grade_level}")
Expected Output:
Name: Alice Johnson | Grade: 11
Question 32: Constructor Chaining
Implement a parent class BaseDatabase with an attribute host. Create a child class PostgreSQLDatabase that takes both a host and a port argument, using super() to chain the constructors.
main.pyRunnableclass BaseDatabase: def __init__(self, host): self.host = host class PostgreSQLDatabase(BaseDatabase): def __init__(self, host, port): # Call base class constructor super().__init__(host) self.port = int(port) ## Verification db = PostgreSQLDatabase("localhost", 5432) print(f"DB Connection string: {db.host}:{db.port}")
Expected Output:
DB Connection string: localhost:5432
Question 33: Method Overriding
Define a base class SchoolActivity containing a method get_fees(). Define a subclass ArtsClub that overrides get_fees() to return a club-specific value.
main.pyRunnableclass SchoolActivity: def get_fees(self): """Default base fees""" return 10.0 class ArtsClub(SchoolActivity): def get_fees(self): """Overrides base get_fees method""" return 25.0 ## Verification activity = SchoolActivity() club = ArtsClub() print(f"Base fees: ${activity.get_fees()}") print(f"Arts Club fees: ${club.get_fees()}")
Expected Output:
Base fees: $10.0 Arts Club fees: $25.0
Question 34: Multi-Level Inheritance
Define a multi-level inheritance hierarchy: base class Person, child class Employee, and grandchild class DepartmentManager. Each class must add a new attribute.
main.pyRunnableclass Person: def __init__(self, name): self.name = name class Employee(Person): def __init__(self, name, employee_id): super().__init__(name) self.employee_id = employee_id class DepartmentManager(Employee): def __init__(self, name, employee_id, department): super().__init__(name, employee_id) self.department = department ## Verification mgr = DepartmentManager("Alice Johnson", "E-804", "Engineering") print(f"Manager: {mgr.name} | ID: {mgr.employee_id} | Dept: {mgr.department}")
Expected Output:
Manager: Alice Johnson | ID: E-804 | Dept: Engineering
Question 35: Multiple Inheritance
Define multiple inheritance by creating a Camera class and a Phone class, and combining them into a single SmartPhone class that inherits from both.
main.pyRunnableclass Camera: def take_photo(self): return "Capturing high-resolution photo." class Phone: def make_call(self): return "Initiating voice connection..." class SmartPhone(Camera, Phone): """Multiple Inheritance""" pass ## Verification phone = SmartPhone() print(phone.take_photo()) print(phone.make_call())
Expected Output:
Capturing high-resolution photo. Initiating voice connection...
Question 36: Method Resolution Order (MRO)
Inspect the Method Resolution Order (MRO) of a multiple inheritance structure using the magic attribute __mro__ to verify Python's method lookup order.
main.pyRunnableclass BaseNode: def resolve(self): return "BaseNode resolution" class NodeA(BaseNode): def resolve(self): return "NodeA resolution" class NodeB(BaseNode): def resolve(self): return "NodeB resolution" class LeafNode(NodeA, NodeB): """Leaf node inheriting from both NodeA and NodeB""" pass leaf = LeafNode() print(f"Lookup resolution: {leaf.resolve()}") ## Print MRO tuple for cls in LeafNode.__mro__: print(f" -> {cls.__name__}")
Expected Output:
Lookup resolution: NodeA resolution -> LeafNode -> NodeA -> NodeB -> BaseNode -> object
Question 37: Abstract Base Classes (ABCs)
Define an Abstract Base Class (ABC) named AbstractEvaluator containing an @abstractmethod named evaluate(). Create a subclass PerformanceEvaluator that implements it.
main.pyRunnablefrom abc import ABC, abstractmethod class AbstractEvaluator(ABC): @abstractmethod def evaluate(self): """Abstract method; must be implemented by subclasses""" pass class PerformanceEvaluator(AbstractEvaluator): def evaluate(self): return "Evaluating metrics using KPI models." ## Verification try: # Attempting to instantiate the abstract class directly raises a TypeError invalid = AbstractEvaluator() except TypeError as error: print(f"Instantiation failed: {error}") evaluator = PerformanceEvaluator() print(f"Evaluator result: {evaluator.evaluate()}")
Expected Output:
Instantiation failed: Can't instantiate abstract class AbstractEvaluator with abstract method evaluate Evaluator result: Evaluating metrics using KPI models.
Question 38: Polymorphism with Method Overriding
Demonstrate polymorphism by passing different shapes (Circle and Square) to a generic print_area(shape_object) function that calls the polymorphic calculate_area() method on each shape.
main.pyRunnableclass Shape: def calculate_area(self): return 0.0 class Circle(Shape): def __init__(self, radius): self.radius = radius def calculate_area(self): return 3.14159 * (self.radius ** 2) class Square(Shape): def __init__(self, side): self.side = side def calculate_area(self): return self.side * self.side def print_area(shape_object): """Polymorphic helper function""" print(f"Shape area: {shape_object.calculate_area():.2f}") ## Verification print_area(Circle(5.0)) print_area(Square(4.0))
Expected Output:
Shape area: 78.54 Shape area: 16.00
Question 39: Polymorphic collections
Store instances of different evaluator subclasses in a single list and process them using a loop, invoking their shared method interface.
main.pyRunnableclass StaffMember: def __init__(self, name): self.name = name def calculate_bonus(self): return 0.0 class TeacherMember(StaffMember): def calculate_bonus(self): return 500.0 class SupportMember(StaffMember): def calculate_bonus(self): return 300.0 ## Create a polymorphic collection staff_list = [TeacherMember("Alice"), SupportMember("Bob"), TeacherMember("Charlie")] ## Process uniformly using polymorphism for member in staff_list: print(f"Staff: {member.name} | Bonus: ${member.calculate_bonus()}")
Expected Output:
Staff: Alice | Bonus: $500.0 Staff: Bob | Bonus: $300.0 Staff: Charlie | Bonus: $500.0
Question 40: Duck Typing in Python
Demonstrate Duck Typing in Python by defining two unrelated classes, Airplane and MallardBird, which both implement a fly() method, and processing them using a shared function.
main.pyRunnableclass Airplane: def fly(self): return "Airplane flying using twin jet engines." class MallardBird: def fly(self): return "Mallard bird flying by flapping wings." def launch_flight(flying_object): """Duck typing: calls fly() on any object that provides the method""" print(flying_object.fly()) ## Verification launch_flight(Airplane()) launch_flight(MallardBird())
Expected Output:
Airplane flying using twin jet engines. Mallard bird flying by flapping wings.
Level 4 Checkpoint
Level 5: Advanced Magic Methods and Custom Systems
Question 41: Arithmetic Operator Overloading
Define a class CreditsCohort representing academic credits. Overload the addition + and subtraction - operators to allow direct credit arithmetic between instances.
main.pyRunnableclass CreditsCohort: def __init__(self, credits_value): self.credits_value = float(credits_value) def __add__(self, other): if not isinstance(other, CreditsCohort): raise TypeError("Addition requires another CreditsCohort instance.") return CreditsCohort(self.credits_value + other.credits_value) def __sub__(self, other): if not isinstance(other, CreditsCohort): raise TypeError("Subtraction requires another CreditsCohort instance.") return CreditsCohort(max(0.0, self.credits_value - other.credits_value)) ## Verification c1 = CreditsCohort(15.5) c2 = CreditsCohort(12.0) c3 = c1 + c2 c4 = c1 - c2 print(f"Added Credits: {c3.credits_value}") print(f"Subtracted Credits: {c4.credits_value}")
Expected Output:
Added Credits: 27.5 Subtracted Credits: 3.5
Question 42: Custom repr for Development
Implement both __str__ (for user-friendly display) and __repr__ (for detailed, developer-focused representation) on a class AcademicCourse.
main.pyRunnableclass AcademicCourse: def __init__(self, name, code): self.name = name self.code = code def __str__(self): return f"Course: {self.name} ({self.code})" def __repr__(self): return f"AcademicCourse(name='{self.name}', code='{self.code}')" ## Verification course = AcademicCourse("AP Calculus", "MATH-401") print(f"str(): {course}") print(f"repr(): {repr(course)}")
Expected Output:
str(): Course: AP Calculus (MATH-401) repr(): AcademicCourse(name='AP Calculus', code='MATH-401')
Question 43: Custom Container Length
Define a class RosterCollection that wraps a private list of students, and override the magic __len__ method to return the active student headcount when len() is called on the collection.
main.pyRunnableclass RosterCollection: def __init__(self): self.__students = [] def enroll(self, name): self.__students.append(name) def __len__(self): """Returns the number of students when len() is called on the object""" return len(self.__students) ## Verification roster = RosterCollection() roster.enroll("Alice") roster.enroll("Bob") print(f"Total Enrolled: {len(roster)}")
Expected Output:
Total Enrolled: 2
Question 44: Indexing Overloading (getitem / setitem)
Define a class GradeBook that overloads the magic methods __getitem__ and __setitem__ to allow dictionary-like indexing and assignment on its instance.
main.pyRunnableclass GradeBook: def __init__(self): self.__grades = {} def __getitem__(self, student_name): """Custom index retriever ([key])""" return self.__grades.get(student_name, 0.0) def __setitem__(self, student_name, grade_value): """Custom index assigner ([key] = value)""" self.__grades[student_name] = float(grade_value) ## Verification book = GradeBook() book["Alice"] = 95.5 book["Bob"] = 88.0 print(f"Alice Grade: {book['Alice']}%") print(f"Bob Grade: {book['Bob']}%") print(f"Missing Student Grade: {book['Charlie']}%")
Expected Output:
Alice Grade: 95.5% Bob Grade: 88.0% Missing Student Grade: 0.0%
Question 45: Callable Object Instances
Define a class TaxCalculator that overloads the magic __call__ method, making its instances directly callable like standard functions.
main.pyRunnableclass TaxCalculator: def __init__(self, rate): self.rate = float(rate) def __call__(self, income): """Called when the object instance itself is invoked as a function""" return float(income) * self.rate ## Verification calculate_tax = TaxCalculator(0.15) ## Call instance directly like a function print(f"Calculated Tax: ${calculate_tax(50000.0):.2f}")
Expected Output:
Calculated Tax: $7500.00
Question 46: Custom Context Manager
Define a class DatabaseTransaction that implements __enter__ and __exit__ to act as a custom context manager. It must print transaction initialization and exit operations.
main.pyRunnableclass DatabaseTransaction: def __enter__(self): print("[Transaction] Beginning isolation level lock.") return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type: print(f"[Transaction] Rollback triggered due to error: {exc_val}") else: print("[Transaction] Commit successful. Releasing locks.") return True # Suppress exceptions if True ## Verification with DatabaseTransaction(): print("Executing operations within transaction context.")
Expected Output:
[Transaction] Beginning isolation level lock. Executing operations within transaction context. [Transaction] Commit successful. Releasing locks.
Question 47: Object Sortable Comparisons
Define a class StudentGPA with attributes name and gpa. Overload the magic methods __eq__ (equality) and __lt__ (less than) to allow sorting student list objects by GPA automatically.
main.pyRunnableclass StudentGPA: def __init__(self, name, gpa): self.name = name self.gpa = float(gpa) def __eq__(self, other): if not isinstance(other, StudentGPA): return False return self.gpa == other.gpa def __lt__(self, other): if not isinstance(other, StudentGPA): raise TypeError("Comparison requires another StudentGPA instance.") # Reverse less-than order so sorting yields descending list order return self.gpa > other.gpa ## Verification students = [ StudentGPA("Alice", 3.6), StudentGPA("Bob", 3.9), StudentGPA("Charlie", 3.8) ] students.sort() # Uses __lt__ under the hood for s in students: print(f" -> {s.name}: {s.gpa}")
Expected Output:
-> Bob: 3.9 -> Charlie: 3.8 -> Alice: 3.6
Question 48: Dynamic Attribute Interceptions
Define a class GracefulSafe that implements __getattr__ and __setattr__. The class must intercept attribute requests and return "Attribute Missing" instead of throwing an AttributeError.
main.pyRunnableclass GracefulSafe: def __init__(self): # Access self.__dict__ directly inside __setattr__ to avoid infinite recursion super().__setattr__("values", {}) def __getattr__(self, name): """Intercepts failed attribute lookups gracefully""" return f"[GracefulSafe] Attribute '{name}' does not exist." def __setattr__(self, name, value): """Intercepts all attribute assignment attempts""" print(f"[Audit] Intercepted assignment: {name} = {value}") super().__setattr__(name, value) ## Verification safe = GracefulSafe() safe.locker_id = "L-300" print(f"Status: {safe.status}")
Expected Output:
[Audit] Intercepted assignment: locker_id = L-300 Status: [GracefulSafe] Attribute 'status' does not exist.
Question 49: Custom Iterator Object
Define a class FibonacciRange that implements __iter__ and __next__ to create a custom iterator object that yields Fibonacci numbers up to a specified limit.
main.pyRunnableclass FibonacciRange: def __init__(self, limit): self.limit = limit self.count = 0 self.a, self.b = 0, 1 def __iter__(self): return self def __next__(self): if self.count >= self.limit: raise StopIteration value = self.a self.a, self.b = self.b, self.a + self.b self.count += 1 return value ## Verification fib = FibonacciRange(5) print(list(fib))
Expected Output:
[0, 1, 1, 2, 3]
Question 50: Comprehensive Academic Roster & JSON Database
Create a fully integrated, robust OOP database system. Combine:
Coursecomposed ofStudentandTeacherobjects (inheriting fromSchoolMember).- Implement property validations, class methods, list sorting, custom
__str__representations, and exception shields. - Write a
DatabaseManagerclass that serializes the data to a JSON file and reconstructs the objects back on load.
main.pyRunnable## comprehensive_oop_database.py import json import pathlib class SchoolMember: def __init__(self, name, email): if not name or not isinstance(name, str): raise TypeError("Name must be a non-empty string.") self.name = name.strip() self.email = email.strip() class Student(SchoolMember): def __init__(self, name, email, gpa): super().__init__(name, email) self.gpa = gpa # Invokes property setter @property def gpa(self): return self.__gpa @gpa.setter def gpa(self, value): if not isinstance(value, (int, float)) or not (0.0 <= value <= 4.0): raise ValueError("GPA must range between 0.0 and 4.0.") self.__gpa = float(value) def __str__(self): return f"Student: {self.name} (GPA: {self.gpa:.2f})" def __lt__(self, other): # Sort by descending GPA return self.gpa > other.gpa class Teacher(SchoolMember): def __init__(self, name, email, department): super().__init__(name, email) self.department = department def __str__(self): return f"Faculty: {self.name} ({self.department})" class Course: def __init__(self, name, code): self.name = name self.code = code self.instructor = None self.__roster = [] def assign_instructor(self, teacher): if not isinstance(teacher, Teacher): raise TypeError("Must assign a valid Teacher object.") self.instructor = teacher def enroll(self, student): if not isinstance(student, Student): raise TypeError("Can only enroll Student objects.") self.__roster.append(student) def get_roster(self): return list(self.__roster) def print_course_status(self): print(f"\nCourse: {self.name} ({self.code})") print(f"Instructor: {self.instructor}") print("Roster:") # Sort roster by GPA dynamically sorted_roster = sorted(self.__roster) for s in sorted_roster: print(f" * {s}") class DatabaseManager: def __init__(self, file_path): self.path = pathlib.Path(file_path) def save(self, course): payload = { "name": course.name, "code": course.code, "instructor": { "name": course.instructor.name, "email": course.instructor.email, "department": course.instructor.department } if course.instructor else None, "roster": [ {"name": s.name, "email": s.email, "gpa": s.gpa} for s in course.get_roster() ] } with open(self.path, "w", encoding="utf-8") as f: json.dump(payload, f, indent=4) def load(self): if not self.path.exists(): return None with open(self.path, "r", encoding="utf-8") as f: data = json.load(f) course = Course(data["name"], data["code"]) ins = data.get("instructor") if ins: course.assign_instructor(Teacher(ins["name"], ins["email"], ins["department"])) for s in data.get("roster", []): course.enroll(Student(s["name"], s["email"], s["gpa"])) return course ## Verification db_manager = DatabaseManager("catalog.json") ## Create system instances prof = Teacher("Dr. Alan Jones", "ajones@school.edu", "Physics") course = Course("AP Physics", "PHYS-101") course.assign_instructor(prof) course.enroll(Student("Alice Johnson", "alice@school.edu", 3.9)) course.enroll(Student("Bob Smith", "bob@school.edu", 3.6)) ## Save Course details to disk db_manager.save(course) ## Reconstruct system from disk backup loaded_course = db_manager.load() loaded_course.print_course_status() ## Clean up created files if db_manager.path.exists(): db_manager.path.unlink()
Expected Output:
Course: AP Physics (PHYS-101) Instructor: Faculty: Dr. Alan Jones (Physics) Roster: * Student: Alice Johnson (GPA: 3.90) * Student: Bob Smith (GPA: 3.60)
Level 5 Checkpoint
Finished this lesson?
Mark it complete to advance your curriculum progress.