Using a Prototype Registry in Python

Using a Prototype Registry in Python

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.

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.

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.

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.

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().

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.

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.

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.

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.

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 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.

Further Reading

If you want the broader “when does Prototype help?” article next, read When the Prototype Pattern Starts Making Sense .

If you want the technical follow-up on clone behavior next, read Shallow Copy vs Deep Copy in the Prototype Pattern .

Raell Dottin

Comments