The Prototype pattern — creating new objects by copying an existing one

Constructing a new object from scratch means re-running every step of __init__ — Prototype instead copies an already fully-configured object, which is both cheaper when construction is genuinely expensive and the only real option when the concrete class to instantiate isn't known ahead of time.

Intermediate

3 min read

The problem: constructing an object from scratch re-does expensive setup every time

class GameCharacter:
    def __init__(self, sprite_sheet_path, stats_config):
        self.sprite = load_and_parse_sprite_sheet(sprite_sheet_path)  # genuinely EXPENSIVE — disk I/O, parsing
        self.stats = deep_process_stats(stats_config)                   # also real, non-trivial work
        self.position = (0, 0)
 
# Spawning 50 identical enemies means re-running the EXPENSIVE setup 50 separate times
enemies = [GameCharacter("goblin.png", goblin_stats) for _ in range(50)]

Every GameCharacter() call re-does the full, genuinely expensive setup (loading and parsing a sprite sheet from disk, processing a stats configuration) — spawning fifty identical goblins means paying that real cost fifty separate times, even though forty-nine of those fifty results are, other than position, completely identical to the first.

The fix: build one fully-configured object once, then copy it

import copy
 
class GameCharacter:
    def __init__(self, sprite_sheet_path, stats_config):
        self.sprite = load_and_parse_sprite_sheet(sprite_sheet_path)
        self.stats = deep_process_stats(stats_config)
        self.position = (0, 0)
 
    def clone(self):
        return copy.deepcopy(self)  # copies the ALREADY-DONE expensive work, not re-does it
 
goblin_prototype = GameCharacter("goblin.png", goblin_stats)  # expensive setup runs ONCE
enemies = [goblin_prototype.clone() for _ in range(50)]         # 50 CHEAP copies, no re-parsing
enemies[3].position = (100, 200)  # each clone is independent — safe to modify without affecting the others

goblin_prototype pays the real, expensive setup cost exactly once — every subsequent enemy is produced by copy.deepcopy(), which duplicates the already-fully-built object's data directly, without re-running __init__'s expensive work at all. copy.deepcopy (from this platform's Python domain) is essential here specifically — a shallow copy would leave every clone sharing the exact same underlying sprite/stats objects, which is fine for genuinely shared, read-only data but dangerous the moment any clone needs its own independently-mutable state.

The other real use: copying when you don't know the concrete class in advance

def spawn_wave(prototype, count):
    return [prototype.clone() for _ in range(count)]  # works for ANY prototype, regardless of its actual class
 
goblin_wave = spawn_wave(goblin_prototype, 10)
dragon_wave = spawn_wave(dragon_prototype, 2)  # spawn_wave never needed to know "Dragon" exists

spawn_wave never references GameCharacter, Goblin, or Dragon by name — it just calls .clone() on whatever prototype it's given, which works correctly regardless of the prototype's actual concrete class, as long as that class implements clone(). This is genuinely useful when the specific class to instantiate is only known at runtime (loaded from a config file, chosen by user input) — constructing a fresh instance normally would need to know which class's constructor to call by name, while cloning a prototype only needs a reference to an already-existing example.

How this differs from Factory, since both are ways of producing new instances

Factory (from its own lesson) decides which class to instantiate and constructs a genuinely new instance from scratch, typically based on some input (a type string, a config value) — the factory contains the logic for choosing and building. Prototype sidesteps the "which class, and how do I construct it" question entirely by copying an object that's already fully built and configured — no constructor logic needs to run again, and the code doing the cloning doesn't need to know anything about how the original was originally built.

The concrete signal a Prototype belongs somewhere

The tell: creating a new instance from scratch is genuinely expensive (real I/O, heavy computation, complex configuration), many instances will be near-identical copies of a known-good starting point, or the specific concrete class to produce isn't known until runtime and only an existing example object is available to work from.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. What real cost does the Prototype pattern avoid when spawning many similar objects?

2. Why is copy.deepcopy() specifically needed for cloning, rather than a shallow copy?

3. How does Prototype differ from Factory, since both produce new object instances?