Easy Types in Python: the Prototype Pattern

Introduction

The prototype-pattern is a creational design pattern that allows us to create new objects by cloning existing ones, i.e. the existing objects function as a kind of template. This can save time in some cases when for example an object creation involves some heavy calculation or things like network traffic or database queries.

So what does it look like?

A short explanation:

  1. The main part is the Prototype interface. This defines a Clone() method which returns a Prototype. We will see in the implementation why this is important.
  2. There are some concrete classes which implement the Clone() method. In this example there are two, but this could of course be any number.
  3. Finally there is the Client, the class which needs the concrete classes.

Implementation in Python

We will start with these preliminaries:

from typing import Self
from abc import ABC, abstractmethod
import copy

In this article you will find an explanation about Self, which is the type of the enclosing class of the method. This will come in very handy as you will see.

The second import from ‘abc’ imports some essential components for creating abstract base classes. ABC which stands for ‘Abstract Base Class’, enables us to define abstract base classes, while @abstractmethod is a decorator used to mark methods as abstract, i.e. which must be implemented in subclasses.

The third import copy brings in python copying utilities. In our example we will be using copy.deepcopy() for deep copying objects.

The Prototype class

Next we define the Prototype base class:

class Prototype(ABC):

    
    @abstractmethod
    def clone(self) -> Self:
        pass

The clone() method will return a copy of the class, that is an object of the type of the enclosing class. That is why we have Self as the return type.

Note that the Prototype class derives from the ABC class, making it abstract. Also the clone() method is defined as an @abstractmethod so it needs to be implemented by the subclasses of Prototype.

The concrete types

The first subclass implementing the Prototype will be a Document class:

class Document(Prototype):
    
    def __init__(self, title: str, content: str, tags: list[str]):
        self.title = title
        self.content = content
        self.tags = tags.copy()  # Create a copy to avoid shared references
        print(f"📄 Created document: {title}")
    
    def clone(self) -> Self:
        print(f"📋 Cloning document: {self.title}")
        return copy.deepcopy(self)
    
    def add_tag(self, tag: str):
        self.tags.append(tag)
    
    def set_title(self, title: str):
        self.title = title
        
    def __str__(self) -> str:
        return f"'{self.title}' - Tags: {self.tags}"

Some notes:

  1. A document has several properties: a title, some content and some tages.
  2. We implement several utility methods to set the title, and add tags.
  3. The clone() method simply calls the deepcopy() method to make a deepcopy of self and returns this copy.

The Shape class works along the same line:

class Shape(Prototype):
    
    def __init__(self, color: str, position: dict[str, int]):
        self.color = color
        self.position = position.copy()  # Copy to avoid shared references
        print(f"🎨 Created {color} shape at {position}")
    
    def clone(self) -> Self:

        print(f"📋 Cloning {self.color} shape")
        return copy.deepcopy(self)
    
    def move(self, x: int, y: int):

        self.position['x'] = x
        self.position['y'] = y
    
    def change_color(self, color: str):
        self.color = color
        
    def __str__(self) -> str:
        return f"{self.color} shape at ({self.position['x']}, {self.position['y']})"

Time to test

We can now test the code:

if __name__ == "__main__":
   

    original_doc = Document("Report Template", "This is a template", ["template", "work"])
    

    monthly_report = original_doc.clone()
    monthly_report.set_title("Monthly Sales Report")
    monthly_report.add_tag("sales")
    monthly_report.add_tag("monthly")
    
    quarterly_report = original_doc.clone()
    quarterly_report.set_title("Quarterly Review")
    quarterly_report.add_tag("quarterly")
    quarterly_report.add_tag("review")
    
    print(f"Original: {original_doc}")
    print(f"Monthly:  {monthly_report}")
    print(f"Quarterly: {quarterly_report}")

    original_shape = Shape("red", {"x": 0, "y": 0})
    

    shape1 = original_shape.clone()
    shape1.move(10, 20)
    shape1.change_color("blue")
    
    shape2 = original_shape.clone()
    shape2.move(30, 40)
    shape2.change_color("green")
    
    print(f"Original: {original_shape}")
    print(f"Shape 1:  {shape1}")
    print(f"Shape 2:  {shape2}")

Some notes:

  • We start by creating a document, the original document, and cloning it twice, setting the title and adding tags to the clones. Finally we print out these documents, showing the titles and the tags.
  • Next we do the same thing for shapes, and print them out.

Conclusion

The prototype pattern is a simple but powerful way to create new objects efficiently by cloning existing ones instead of building them from scratch. Especially in situations where initialization is expensive — involving heavy computation, network calls, or database access — this pattern can save both time and resources.

In our Python example, we’ve seen how to set up an abstract Prototype base class, implement concrete prototypes like Document and Shape, and use copy.deepcopy() to create clean, independent copies. This approach keeps your code flexible, avoids duplication, and makes object creation faster and more maintainable.

In short, the prototype pattern is all about reusing what you already have — smartly.

The Code Nomad
The Code Nomad
Articles: 167

Leave a Reply

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