Prototype is a creational design pattern that allows cloning objects, even complex ones, without coupling to their specific classes.
All prototype classes should have a common interface that makes it possible to copy objects even if their concrete classes are unknown. Prototype objects can produce full copies since objects of the same class can access each other’s private fields.
Complexity:
Popularity:
Usage examples: The Prototype pattern is available in Python out of the box with a copy
module.
Identification: The prototype can be easily recognized by a clone
or copy
methods, etc.
Conceptual Example
This example illustrates the structure of the Prototype design pattern. It focuses on answering these questions:
What classes does it consist of?
What roles do these classes play?
In what way the elements of the pattern are related?
main.py: Conceptual example
import copy
class SelfReferencingEntity:
def __init__(self):
self.parent = None
def set_parent(self, parent):
self.parent = parent
class SomeComponent:
"""
Python provides its own interface of Prototype via `copy.copy` and
`copy.deepcopy` functions. And any class that wants to implement custom
implementations have to override `__copy__` and `__deepcopy__` member
functions.
"""
def __init__(self, some_int, some_list_of_objects, some_circular_ref):
self.some_int = some_int
self.some_list_of_objects = some_list_of_objects
self.some_circular_ref = some_circular_ref
def __copy__(self):
"""
Create a shallow copy. This method will be called whenever someone calls
`copy.copy` with this object and the returned value is returned as the
new shallow copy.
"""
# First, let's create copies of the nested objects.
some_list_of_objects = copy.copy(self.some_list_of_objects)
some_circular_ref = copy.copy(self.some_circular_ref)
# Then, let's clone the object itself, using the prepared clones of the
# nested objects.
new = self.__class__(
self.some_int, some_list_of_objects, some_circular_ref
)
new.__dict__.update(self.__dict__)
return new
def __deepcopy__(self, memo=None):
"""
Create a deep copy. This method will be called whenever someone calls
`copy.deepcopy` with this object and the returned value is returned as
the new deep copy.
What is the use of the argument `memo`? Memo is the dictionary that is
used by the `deepcopy` library to prevent infinite recursive copies in
instances of circular references. Pass it to all the `deepcopy` calls
you make in the `__deepcopy__` implementation to prevent infinite
recursions.
"""
if memo is None:
memo = {}
# First, let's create copies of the nested objects.
some_list_of_objects = copy.deepcopy(self.some_list_of_objects, memo)
some_circular_ref = copy.deepcopy(self.some_circular_ref, memo)
# Then, let's clone the object itself, using the prepared clones of the
# nested objects.
new = self.__class__(
self.some_int, some_list_of_objects, some_circular_ref
)
new.__dict__ = copy.deepcopy(self.__dict__, memo)
return new
if __name__ == "__main__":
list_of_objects = [1, {1, 2, 3}, [1, 2, 3]]
circular_ref = SelfReferencingEntity()
component = SomeComponent(23, list_of_objects, circular_ref)
circular_ref.set_parent(component)
shallow_copied_component = copy.copy(component)
# Let's change the list in shallow_copied_component and see if it changes in
# component.
shallow_copied_component.some_list_of_objects.append("another object")
if component.some_list_of_objects[-1] == "another object":
print(
"Adding elements to `shallow_copied_component`'s "
"some_list_of_objects adds it to `component`'s "
"some_list_of_objects."
)
else:
print(
"Adding elements to `shallow_copied_component`'s "
"some_list_of_objects doesn't add it to `component`'s "
"some_list_of_objects."
)
# Let's change the set in the list of objects.
component.some_list_of_objects[1].add(4)
if 4 in shallow_copied_component.some_list_of_objects[1]:
print(
"Changing objects in the `component`'s some_list_of_objects "
"changes that object in `shallow_copied_component`'s "
"some_list_of_objects."
)
else:
print(
"Changing objects in the `component`'s some_list_of_objects "
"doesn't change that object in `shallow_copied_component`'s "
"some_list_of_objects."
)
deep_copied_component = copy.deepcopy(component)
# Let's change the list in deep_copied_component and see if it changes in
# component.
deep_copied_component.some_list_of_objects.append("one more object")
if component.some_list_of_objects[-1] == "one more object":
print(
"Adding elements to `deep_copied_component`'s "
"some_list_of_objects adds it to `component`'s "
"some_list_of_objects."
)
else:
print(
"Adding elements to `deep_copied_component`'s "
"some_list_of_objects doesn't add it to `component`'s "
"some_list_of_objects."
)
# Let's change the set in the list of objects.
component.some_list_of_objects[1].add(10)
if 10 in deep_copied_component.some_list_of_objects[1]:
print(
"Changing objects in the `component`'s some_list_of_objects "
"changes that object in `deep_copied_component`'s "
"some_list_of_objects."
)
else:
print(
"Changing objects in the `component`'s some_list_of_objects "
"doesn't change that object in `deep_copied_component`'s "
"some_list_of_objects."
)
print(
f"id(deep_copied_component.some_circular_ref.parent): "
f"{id(deep_copied_component.some_circular_ref.parent)}"
)
print(
f"id(deep_copied_component.some_circular_ref.parent.some_circular_ref.parent): "
f"{id(deep_copied_component.some_circular_ref.parent.some_circular_ref.parent)}"
)
print(
"^^ This shows that deepcopied objects contain same reference, they "
"are not cloned repeatedly."
)
Output.txt: Execution result
Adding elements to `shallow_copied_component`'s some_list_of_objects adds it to `component`'s some_list_of_objects.
Changing objects in the `component`'s some_list_of_objects changes that object in `shallow_copied_component`'s some_list_of_objects.
Adding elements to `deep_copied_component`'s some_list_of_objects doesn't add it to `component`'s some_list_of_objects.
Changing objects in the `component`'s some_list_of_objects doesn't change that object in `deep_copied_component`'s some_list_of_objects.
id(deep_copied_component.some_circular_ref.parent): 4429472784
id(deep_copied_component.some_circular_ref.parent.some_circular_ref.parent): 4429472784
^^ This shows that deepcopied objects contain same reference, they are not cloned repeatedly.