Lesson 3: Polymorphism and Encapsulation

In this lesson, we will explore two fundamental concepts of Object-Oriented Programming (OOP): Polymorphism and Encapsulation. These concepts help in organizing and structuring code more efficiently, allowing for flexible and maintainable software.

Additionally, we will touch upon Abstraction and how it is implemented using abstract classes. By the end of this lesson, you’ll have a deep understanding of these OOP principles and how to apply them in Python.

1. Polymorphism Concepts

Polymorphism is derived from two Greek words: poly meaning “many” and morph meaning “form”. It refers to the ability of one entity (such as a method, function, or object) to take on many forms. In Python, polymorphism allows objects of different classes to be treated as objects of a common base class, enabling you to call the same method on different objects with different behaviors.

Polymorphism is typically achieved through method overriding and duck typing in Python.

Types of Polymorphism:

  1. Method Overriding (Runtime Polymorphism):
    • This occurs when a derived class provides a specific implementation of a method that is already defined in its base class.
    • The method in the derived class overrides the base class’s method.
Example: Method Overriding:
python
class Animal:

def speak(self):

print(“Animal makes a sound”)

 

class Dog(Animal):

def speak(self):

print(“Dog barks”)

 

class Cat(Animal):

def speak(self):

print(“Cat meows”)

 

# Creating objects of Dog and Cat

dog = Dog()

cat = Cat()

 

# Calling the speak method

dog.speak()  # Output: Dog barks

cat.speak()  # Output: Cat meows

 

In this example:

  • Both the Dogand Cat classes override the speak() method from the base Animal class, providing specific behavior.
  1. Duck Typing (Static Polymorphism):
    • Python supports duck typing, which means that the type or class of an object is determined by the methods it implements, not by its inheritance.
    • In simpler terms, if an object behaves like a certain type, it can be treated as that type, even if it doesn’t explicitly inherit from it.
Example: Duck Typing:
python
class Bird:

def speak(self):

print(“Bird chirps”)

 

class Airplane:

def speak(self):

print(“Airplane makes a sound”)

 

def make_sound(animal):

animal.speak()

 

# Creating objects of Bird and Airplane

bird = Bird()

plane = Airplane()

 

# Both objects can be passed to the same function because they have a speak method

make_sound(bird)   # Output: Bird chirps

make_sound(plane)  # Output: Airplane makes a sound

 

Here, both Bird and Airplane don’t share any inheritance, but since they both implement the speak() method, they are both accepted by the make_sound() function. This is duck typing in action.

2. Encapsulation and Private Members

Encapsulation is the practice of bundling the data (attributes) and methods that operate on the data into a single unit, or class. It restricts direct access to some of an object’s components, which helps prevent unintended modifications to the object’s state.

In Python, encapsulation is achieved using private attributes and getter/setter methods.

Private Members:

  • Attributes and methods can be made privateby prefixing them with two underscores (__).
  • Private members cannot be accessed directly outside the class, ensuring that the internal state is protected.
Example: Private Members:
python
class Car:

def __init__(self, make, model):

self.make = make  # Public attribute

self.__model = model  # Private attribute

 

# Getter method for private attribute

def get_model(self):

return self.__model

 

# Setter method for private attribute

def set_model(self, model):

self.__model = model

 

# Creating an object of Car

car = Car(“Toyota”, “Camry”)

 

# Accessing public attribute

print(car.make)  # Output: Toyota

 

# Trying to access private attribute directly (will raise an error)

# print(car.__model)  # AttributeError: ‘Car’ object has no attribute ‘__model’

 

# Accessing private attribute using the getter method

print(car.get_model())  # Output: Camry

 

# Modifying the private attribute using the setter method

car.set_model(“Corolla”)

print(car.get_model())  # Output: Corolla

 

In this example:

  • __modelis a private attribute of the Car Direct access to it outside the class is not allowed.
  • The get_model()and set_model() methods are used to get and set the value of the private attribute, providing controlled access.

Benefits of Encapsulation:

  1. Data protection: Private members prevent unauthorized modification of an object’s internal state.
  2. Modularity: The internal workings of the class are hidden, and only necessary information is exposed.
  3. Maintenance: By controlling access to attributes, you can easily change the internal implementation without affecting external code.

3. Abstraction with Abstract Classes

Abstraction is the concept of hiding the complex implementation details of an object and exposing only the necessary and relevant information. This allows users to interact with objects at a higher level, without needing to understand the intricacies of how they work.

In Python, abstract classes are used to define common interfaces for a group of subclasses. Abstract classes cannot be instantiated on their own; they must be subclassed by concrete classes that implement the abstract methods.

Abstract Base Class (ABC):

  • The abcmodule in Python provides the necessary tools for defining abstract classes and abstract methods.
  • Abstract methods are methods that are declared but contain no implementation in the abstract class. They must be implemented by any concrete subclass.
Example: Abstract Classes:
python
from abc import ABC, abstractmethod

 

class Animal(ABC):

@abstractmethod

def speak(self):

pass  # No implementation here, subclasses must implement this method

 

class Dog(Animal):

def speak(self):

print(“Dog barks”)

 

class Cat(Animal):

def speak(self):

print(“Cat meows”)

 

# Trying to create an object of Animal will raise an error

# animal = Animal()  # TypeError: Can’t instantiate abstract class Animal with abstract methods speak

 

# Creating objects of Dog and Cat

dog = Dog()

cat = Cat()

 

dog.speak()  # Output: Dog barks

cat.speak()  # Output: Cat meows

 

In this example:

  • Animalis an abstract class with an abstract method speak().
  • Dogand Cat are concrete classes that provide their own implementation of the speak()
  • We cannot instantiate an object of the abstract class Animal Instead, we must instantiate objects of Dogor Cat, which provide implementations for the abstract methods.

Benefits of Abstraction:

  1. Simplified Interface: Abstract classes provide a clear and simplified interface for other classes to implement.
  2. Code Reusability: Common functionality is implemented in the abstract class, and subclasses can focus on specific implementations.
  3. Flexibility: Abstract classes allow you to define a general interface while leaving room for specific details to be implemented by subclasses.

4. Conclusion

In this lesson, we have covered:

  • Polymorphism: The ability for different classes to provide specific implementations of the same method, either through method overridingor duck typing.
  • Encapsulation: The bundling of data and methods together and restricting direct access to the internal state through private attributesand getter/setter methods.
  • Abstraction: The practice of hiding the complexity of the implementation and exposing only the essential features, achieved in Python using abstract classes.

These concepts are foundational to object-oriented programming and help create cleaner, more efficient, and more maintainable code. In the next lesson, we will dive deeper into more advanced topics in object-oriented programming, such as multiple inheritance and composition.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *