Inheritance
On this page
Define the Base ClassAdd a Student SubclassAdd a Professor SubclassUse Inherited ClassesReuse an Internal AttributeCoding Challenge: SavingsAccountInheritance defines a specialized class from an existing one. The base class supplies reusable behavior; a subclass can add attributes or override methods.
In UML, a solid line with a hollow triangle points from a subclass toward its base class. The compartments list attributes and operations; + marks public members.
Define the Base Class
class User:
def __init__(self, user_id, name, email):
self.user_id = str(user_id)
self.name = name
self.email = email
def profile(self):
return f"{self.name} <{self.email}>"
def role(self):
return "user"Defines User; no visible output.
User stores the common fields and provides profile and role.
Add a Student Subclass
Student(User) names the base class. super() finds the next implementation in Python’s method resolution order; in this hierarchy, super().__init__ reaches User’s initializer.
class Student(User):
def __init__(self, user_id, name, email):
super().__init__(user_id, name, email)
self.courses = []
def role(self):
return "student"
def enroll(self, course_code):
if course_code not in self.courses:
self.courses.append(course_code)Defines Student; no visible output.
The same student object receives the common fields and its own courses list. Overriding role gives it a more specific label.
Add a Professor Subclass
class Professor(User):
def __init__(self, user_id, name, email):
super().__init__(user_id, name, email)
self.teaches = []
def role(self):
return "professor"
def assign_course(self, course_code):
if course_code not in self.teaches:
self.teaches.append(course_code)Defines Professor; no visible output.
Professor reuses User’s initialization and adds its own teaching list.
Use Inherited Classes
Run the definitions above, then:
s1 = Student(101, "Anna", "anna@example.com")
p1 = Professor(44, "Dr. Singh", "singh@example.com")
print(s1.profile())
print(p1.profile())
print(s1.role())
print(p1.role())Output
Anna <anna@example.com>
Dr. Singh <singh@example.com>
student
professor
Both objects use the inherited profile method, but their role implementations return different labels.
Reuse an Internal Attribute
The underscore convention was introduced under encapsulation. It also applies when a subclass works with a base class’s internal data.
class Employee:
def __init__(self, name, salary):
self.name = name
self._salary = salary # protected
class Manager(Employee):
def give_raise(self, amount):
self._salary += amount # allowed: subclass can access protected membersDefines Employee, Manager; no visible output.
Manager adds behavior to Employee; it does not introduce enforced access protection.
m = Manager("Alex", 60000)
m.give_raise(5000)
print(m._salary) # still accessible, but client code should use a public operationOutput
65000
The inherited initializer sets 60000; give_raise(5000) changes it to 65000.
Coding Challenge: SavingsAccount
After completing the BankAccount exercise in Encapsulation and running your completed class definition, create a subclass storing an interest rate. Use the inherited balance query and deposit method to add interest; do not bypass them to change the balance.
Exercise starter: complete before running.
class SavingsAccount(BankAccount):
def __init__(self, initial_balance=0, interest_rate=0.01):
# call parent constructor and store a private interest rate
pass
# add a method to apply interestFor the target below, start at 1000, pass interest_rate=0.05, deposit 500 and apply interest once. The starter default of 0.01 would give a different result.
Target output for your solution
Starting balance: 1000
After deposit: 1500
After interest: 1575.0
Explain which methods are inherited and which responsibility the subclass adds.
We can now compare inheritance with composition and decide when a relationship means “is a” or “has a.”