L is the Liskov Substitution Principle (Barbara Liskov): code written for a base type must keep working correctly with a subtype. This is about the contract, not the syntax: if a subclass has the same methods but its behaviour breaks the caller's expectations, the principle is violated.

The classic example: Rectangle and Square

In mathematics a square is a rectangle, so Square(Rectangle) looks natural. But Square also changes the height after set_width, and the Rectangle contract ("set_width leaves the height alone") is broken.

python
class Rectangle:
    def __init__(self, w, h):
        self.w, self.h = w, h

    def set_width(self, w):
        self.w = w

    def set_height(self, h):
        self.h = h

    def area(self):
        return self.w * self.h

class Square(Rectangle):
    def __init__(self, side):
        super().__init__(side, side)

    def set_width(self, w):                # a square keeps its sides equal
        self.w = self.h = w

    def set_height(self, h):
        self.w = self.h = h

def stretch(rect):
    rect.set_width(5)
    rect.set_height(4)
    return rect.area()                     # the caller expects 5 * 4

print(stretch(Rectangle(1, 1)))
print(stretch(Square(1)))
text
20
16
The same calls: Rectangle 20, Square 16
The same calls: Rectangle 20, Square 16

The fix: do not make the square a subclass of the rectangle. Both are siblings under a common Shape contract and each provides area().

python
class Shape:
    def area(self):
        raise NotImplementedError