Mastering SOLID Principles in Python: Understanding Liskov Substitution Principle
Mohammad Mahdi Jafari
Senior Software Engineer @Subconscious AI | Specializing in backend & ML | Innovating with LangChain & LLMs | RAG | Python | FastAPI | Django
In this edition, we are going to dive into the Liskov Substitution Principle (LSP), a fundamental principle in object-oriented programming. If you missed our previous edition, you can catch up here to stay up to date.
But before we continue, if you find my content valuable and want to receive regular updates, don't forget to subscribe to my newsletter! You'll be the first to know about the latest articles, insights, and industry trends. Simply click here to subscribe.
Now, let's dive right in!
LSP: Liskov Substitution Principle
The Liskov Substitution Principle, introduced by Barbara Liskov in 1987, is a crucial principle that governs the behavior of objects in an object-oriented system. Simply put, it states that objects of a superclass should be seamlessly replaceable with objects of its subclass, without causing any issues or compromising the correctness of the program. This means that a subclass should be able to effortlessly substitute its parent class wherever it is expected.
To illustrate this principle, imagine you have a code that operates on a Shape class. The beauty of LSP is that you should be able to substitute this Shape class with any of its subclasses, such as Circle or Rectangle, without encountering any glitches or breaking the existing code.
Consider the following code snippet:
class Rectangle
def __init__(self, width, height):
self.width = width
self.height = height
def calculate_area(self):
return self.width * self.height:
Here, we have a Rectangle class that initializes the width and height attributes of a rectangle and provides a method to calculate its area.
Now, let's introduce a Square class, which is a special type of rectangle with equal width and height.
领英推荐
class Square(Rectangle)
def __init__(self, side):
super().__init__(side, side)
def __setattr__(self, key, value):
super().__setattr__(key, value)
if key in ("width", "height"):
self.__dict__["width"] = value
self.__dict__["height"] = value:
In the provided code snippet, the Square class is defined as a subclass of Rectangle. Its constructor takes the side length of a square as an argument and initializes the width and height attributes of the parent class (Rectangle) using the side length.
To ensure the Square object behaves correctly, a special method called __setattr__() is defined. This method intercepts attribute assignments and sets both the width and height attributes to the same value whenever either one is modified.
However, there is a problem. This implementation violates the Liskov Substitution Principle because you can't seamlessly replace instances of Rectangle with their Square counterparts.
Imagine someone expects a rectangle object in their code. Naturally, they would assume that it exhibits the behavior of a rectangle, including separate width and height attributes. Unfortunately, the Square class in your codebase violates this assumption by altering the expected behavior defined by the object's interface.
To address this issue and apply the Liskov Substitution Principle, let's introduce a base Shape class and make both Rectangle and Square inherit from it:
from abc import ABC, abstractmetho
class Shape(ABC):
@abstractmethod
def calculate_area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def calculate_area(self):
return self.width * self.height
class Square(Shape):
def __init__(self, side):
self.side = side
def calculate_area(self):
return self.side ** 2d
By adhering to the Liskov Substitution Principle and introducing a common base class (Shape), you ensure that objects of different subclasses can be seamlessly interchanged wherever the superclass is expected. Both Rectangle and Square are now siblings, each with their own set of attributes, initializer methods, and potentially more separate behaviors. The only shared aspect between them is the ability to calculate their respective areas.
Using the Liskov Substitution Principle not only leads to cleaner and more maintainable code but also enhances the flexibility and extensibility of your object-oriented system.
I hope this explanation clarifies the Liskov Substitution Principle and its significance in object-oriented programming.
Don't miss out on my future editions where I dive deeper into fascinating topics and provide invaluable insights. By subscribing to my newsletter today, you'll stay informed and be at the forefront of industry trends and advancements. Join the growing community of passionate developers by clicking here to subscribe.
Have any questions or feedback? Let me know! ??
Web Scraping | Anti-Bot Bypassing | Automation | Back-end | DevOps |?Python
1 年Yeah amazing example to understand what is the L of the SOLID principle
Groundbreaker Talents Alumni | Software Engineer | Digitalizing the African Startup Eco-System
1 年Thanks Mahdi,very helpful to a beginner like me!
Great insights!
Software Engineer | Frontend Developer | 2k+ LinkedIn | React.js, Next.js, TypeScript | Created UX-friendly websites | Passionate about interactive UI/UX.
1 年Well explained ??
Frontend Engineer - React | TS | Nextjs | TailwindCSS
1 年Insightful ??