Skip to main content

Using a Prototype Registry in Python

Python · Design Patterns · Object Cloning

Using a Prototype Registry in Python

Prototype Pattern Series

This article is part 1 of 4 in the current sequence.

Main idea

A registry gives known prototypes one organized place to live so the program can select them at runtime.

Why it matters

The pattern becomes more useful when several reusable templates exist and the program must choose among them dynamically.

Key distinction

The registry manages storage and selection. The prototype still provides the starting state.

Introduction

The Prototype Pattern gets more useful when one prototype is no longer enough. Copying a single object is easy to understand. The more interesting question is what happens when a program needs several stored templates and has to choose between them while it is already running.

That is where a prototype registry helps. Instead of cloning whatever object happens to be nearby, the program keeps a collection of known prototypes and selects the right one by name or role. This article is not the general introduction to Prototype, and it is not the copy-behavior article either. Its job is narrower: runtime selection, storage, and cloning through one organized registry.

Why a registry exists

From One Prototype to Several

A single prototype is easy to picture. You have one object, you copy it, and then you customize the copy. But real programs often need different starting shapes for different situations. A document template is not the same as an invoice template. A basic UI preset is not the same as a danger-state preset. Once several known templates exist, the program needs a way to keep them in one place and choose among them cleanly.

That is the job of the registry. It gives the templates a home and gives the rest of the program one place to ask for them.

Implementation

A Simple Prototype Registry

import copy


class PrototypeManager:
    def __init__(self):
        self._prototypes = {}

    def register_prototype(self, name, prototype):
        self._prototypes[name] = prototype

    def clone(self, name, **kwargs):
        prototype = self._prototypes.get(name)

        if prototype is None:
            raise ValueError(f"Prototype '{name}' not found.")

        cloned_object = copy.deepcopy(prototype)
        cloned_object.__dict__.update(kwargs)
        return cloned_object

This changes the shape of the pattern in an important way. The registry owns the collection of templates. The prototype objects still define the starting shape. The caller only needs to know which prototype to request and what values should change after cloning.

Example object

A Small Object to Clone

class PrototypeObject:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f"PrototypeObject instance with name: {self.name}"

This example is intentionally small. The point is not the object itself. The point is the registry’s role in storing and selecting it.

Using it

Registering and Cloning

manager = PrototypeManager()

manager.register_prototype("object1", PrototypeObject("Object 1"))
manager.register_prototype("object2", PrototypeObject("Object 2"))

obj1 = manager.clone("object1", name="Customized Object 1")
obj2 = manager.clone("object2", name="Customized Object 2")

print(obj1)
print(obj2)

The important idea here is not just that the objects were copied. It is that the program was able to choose a prototype at runtime and then customize the clone after the choice was made. That is what separates a registry from a one-off call to deepcopy().

Practical takeaway A registry matters when the program must decide which starting object to use while it is already running, not when the prototype choice is hardcoded in one constructor path.
Why it helps

Why a Registry Helps

A prototype registry is useful when the choice of starting object belongs to the running program, not to one fixed line of constructor code.

That makes it a good fit for document templates, UI presets, configuration templates, seeded workflow objects, and similar situations where several known starting shapes exist and the program needs to select one dynamically.

Without a registry, that selection logic often gets scattered into conditionals across the project. With a registry, the templates are centralized and the selection becomes easier to reason about.

Second half of the pattern

Customization After Cloning Is Part of the Pattern

Beginners sometimes focus only on the copy and forget the second half of the pattern. Prototype is not just about duplication. It is about duplication followed by adjustment. The clone should start from a useful base state, then become the specific object needed for the current case.

That is why the registry’s clone() method updates the copied object with keyword arguments. The copy gives you the base shape. The customization gives you the final object.

Copy behavior

Why Deep Copy Still Matters Here

The registry example uses copy.deepcopy() for a reason. If the prototype contains nested mutable data, a shallow copy can leave the original and the clone tied together in ways that make the registry feel unreliable. A deep copy usually better matches the expectation that each requested object should stand on its own after cloning.

That is not because deep copy is always better in every program. It is because a registry often implies reusable templates that can be cloned many times. Shared nested state usually makes that reuse harder to trust unless it is deliberate.

Boundaries

What the Registry Owns and What It Does Not

The registry owns storage and selection. It does not replace the prototype object itself. The prototype still provides the starting state. The registry simply gives the application a clean way to find the right starting state at the right time.

That distinction matters because it keeps the design readable. If the registry starts becoming a place for unrelated behavior, it stops being a registry and starts becoming a vague manager object.

Related concern

About Using super() in Prototype Code

A smaller question sometimes appears around this pattern: can you use super() inside prototype-related code? Technically, yes. But it is usually not the important question.

super() belongs mainly to inheritance and method resolution. Prototype belongs mainly to copying and cloning. Those concerns can coexist in one project, but they are not the same structural idea. In practice, the clearer question is not “Can I use super() here?” It is “Is this code about inheritance or about cloning?” Mixing those concerns too loosely usually makes the design harder to read.

What to keep

What a Beginner Should Keep

A prototype registry is a useful extension of the Prototype Pattern when a program needs more than one reusable template. It lets you register known prototypes, select one at runtime, clone it, and then customize the clone for the current situation. That is a more practical structure than simply copying one object in isolation.

The important distinction is that the registry manages selection, while the prototype still provides the starting state. Once that is clear, the pattern becomes easier to use without letting it turn into a vague container for everything.

FAQ

Frequently Asked Questions

These are the practical questions beginners usually have when the Prototype Pattern grows from one object into a registry of reusable templates.

What is a prototype registry in simple terms?

It is a place where a program stores known prototype objects so it can choose one later, clone it, and customize the clone at runtime.

How is a registry different from just calling deepcopy() directly?

A direct copy only duplicates one object you already have nearby. A registry adds organized storage and runtime selection across several known templates.

Why would a program need more than one prototype?

Because real programs often need different starting shapes for different cases, such as multiple document templates, UI presets, or workflow seeds.

Why use deepcopy() in the registry example?

Because nested mutable state can otherwise stay shared between the original and the clone. Deep copy usually better matches the expectation that each clone stands on its own.

What does the registry actually own?

The registry owns storage and selection. It does not replace the prototype’s job of defining the starting state.

Why update attributes after cloning?

Because Prototype is not only about duplication. It is also about starting from a useful base and then adjusting the clone for the current case.

Can a registry become a bad design?

Yes, if it starts collecting unrelated behavior and turns into a vague manager object. Its job should stay narrow: store, select, and clone prototypes.

What is the main beginner lesson here?

The registry extends Prototype from “copy one object” into “keep several reusable starting shapes available and choose among them cleanly at runtime.”

Raell Dottin

Comments