How to Create Flexible and Scalable Code Using the draw() Method
Introduction
Imagine you’re building a drawing application with different shapes like Circle, Square, and Triangle. Each shape must be able to draw itself. Now, let’s say you want to create a function render_shapes() that can take a list of any shape objects and call their draw() methods—without checking their types manually.
How do you do this in an elegant, scalable way? The answer lies in polymorphism.

The Core Concept: Polymorphism at Work
Polymorphism allows objects of different classes to be treated as objects of a common superclass. In Python, you don’t even need an explicit superclass to use this feature, thanks to duck typing—if it looks like a duck and quacks like a duck, Python treats it as a duck.
Let’s look at an example:
class Circle:
def draw(self):
print("Drawing a Circle")
class Square:
def draw(self):
print("Drawing a Square")
class Triangle:
def draw(self):
print("Drawing a Triangle")

Each class has a draw() method. Now, you can write the render_shapes() function as:
def render_shapes(shapes):
for shape in shapes:
shape.draw()
You don’t need to check if the object is a Circle, Square, or Triangle. As long as the object has a draw() method, Python will call it. This makes the code simple, clean, and extendable.
If you later add a new class like Hexagon with its own draw() method, you won’t need to change render_shapes() at all.

Conclusion
Using polymorphism and duck typing in Python allows you to write more general and flexible code. The render_shapes() function works with any object that has a draw() method, without knowing or caring about its exact class. This approach not only makes your code shorter and cleaner but also easier to maintain and expand.
So the next time you design classes with similar methods, think polymorphically—it’s the Pythonic way!