Object Composition, Encapsulation, and Polymorphism in Python

Object Composition

Understanding Composition

Composition occurs when an object contains another object as one of its attributes, allowing complex structures to be built from simpler components.

Benefits of Composition

Composition reduces code duplication and enhances program extensibility by promoting code reuse and modular design.

Implementing Composition

Create instances of one class and assign them as attributes to another class instance:

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

class BirthDate:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def display_birth_info(self, person):
        print(f"""
        ==={person.name}'s Birth Details===
        Year: {self.year}
        Month: {self.month}
        Day: {self.day}
        """)

class Instructor(Person):
    pass

class Learner(Person):
    pass

prof = Instructor('John Doe')
b_date = BirthDate(1985, 7, 15)

prof.birth_info = b_date
prof.birth_info.display_birth_info(prof)

Course Enrollment System Example

class Person:
    def __init__(self, name):
        self.name = name
        self.courses = []

    def enroll_course(self, course_obj):
        self.courses.append(course_obj)
        print(f'{self.name} enrolled in {course_obj.name}')

    def show_courses(self):
        print(f'{self.name}\'s enrolled courses:')
        for course in self.courses:
            print(f"""
            Course: {course.name}
            Duration: {course.duration}
            Price: {course.price}
            """)

class Course:
    def __init__(self, name, duration, price):
        self.name = name
        self.duration = duration
        self.price = price

class Student(Person):
    def __init__(self, name, grade):
        super().__init__(name)

class Teacher(Person):
    def __init__(self, name, rank):
        super().__init__(name)

teacher = Teacher('Dr. Smith', 'Senior')
student = Student('Alice', 'A')
python_course = Course('Python', 6, 20000)
linux_course = Course('Linux', 4, 1000)

teacher.enroll_course(python_course)
teacher.enroll_course(linux_course)
teacher.show_courses()

Encapsulation

Concept of Encapsulation

Encapsulation bundles related attributes and methods into objects, controlling access through well-defined interfaces.

Purpose of Encapsulation

Encapsulation organizes code into logical units and protects internal state from unintended modifications.

Implementation Methods

Define attributes and methods within classes, and control access using Python's naming convensions and property decorators.

Access Control Mechanisms

Private Attributes

Prefix attributes with double underscores to indicate they shouldn't be accessed directly from outside the class.

Controlled Access

Provide methods to get and set private attributes with validation logic:

class SecureData:
    __secret_value = 'confidential'

    def get_value(self):
        return self.__secret_value

    def set_value(self, new_val):
        self.__secret_value = new_val

data = SecureData()
print(data.get_value())
data.set_value('updated_secret')
print(data.get_value())

Teacher Information Management Example

class Educator:
    def __init__(self, name, age, gender):
        self.__name = name
        self.__age = age
        self.__gender = gender

    def show_info(self):
        username = input('Username: ')
        password = input('Password: ')
        if username == 'admin' and password == 'secure123':
            print(f"""
            Educator Details:
            Name: {self.__name}
            Age: {self.__age}
            Gender: {self.__gender}
            """)

    def update_info(self, name, age, gender):
        if not all([isinstance(name, str), isinstance(age, int), isinstance(gender, str)]):
            raise ValueError('Invalid input types')
        self.__name = name
        self.__age = age
        self.__gender = gender

educator = Educator('Dr. Brown', 42, 'male')
educator.update_info('Prof. Green', 45, 'female')
educator.show_info()

Property Decorator

Using @property

The @property decorator allows methods to be accessed like attributes while maintaining computation logic:

class HealthMetrics:
    def __init__(self, weight_kg, height_m):
        self.weight = weight_kg
        self.height = height_m

    @property
    def bmi(self):
        return self.weight / (self.height ** 2)

person = HealthMetrics(70, 1.8)
print(person.bmi)

Polymorphism

Polymorphic Behavior

Different classes can implement the same method name with different behaviors:

class Animal:
    def vocalize(self):
        print('Animal sound')

class Canine(Animal):
    def vocalize(self):
        print('Bark')

class Feline(Animal):
    def vocalize(self):
        print('Meow')

dog = Canine()
cat = Feline()
dog.vocalize()
cat.vocalize()

Duck Typing

Python uses duck typing where objects are judged by their methods rather than their types:

class Bird:
    def sound(self):
        print('Chirp')

class Instrument:
    def sound(self):
        print('Play music')

def make_sound(entity):
    entity.sound()

sparrow = Bird()
piano = Instrument()
make_sound(sparrow)
make_sound(piano)

Standardized Interfaces

Create functions that work with any object implementing required methods:

def calculate_length(item):
    return item.__len__()

text = "hello"
numbers = [1, 2, 3, 4]
print(calculate_length(text))
print(calculate_length(numbers))

Tags: python OOP composition encapsulation Polymorphism

Posted on Wed, 05 Aug 2026 16:38:06 +0000 by wolfan