1.4. Prototype Pattern
The Prototype Pattern is used to create new objects by cloning an original object, and then modifying the clone.
As we have already seen, especially in the previous section, Python supports a wide variety of ways of creating new objects, even when their types are only known at runtime—and even if we have only their types’ names.
class Point: __slots__ = (
"y") def __init__(self, x, y): self.x = x self.y = y
Given this classic Point class, here are seven ways to create new points:
def make_object(Class, *args, **kwargs): return Class(*args, **kwargs) point1 = Point(
2) point2 = eval(
# Risky point3 = getattr(sys.modules[__name__],()[
6) point4 = globals
8) point5 = make_object(Point,
10) point6 = copy.deepcopy(point5) point6.x =
12point7 = point1.__class__(
# Could have used any of point1 to point6
Point point1 is created conventionally (and statically) using the Point class object as a constructor.* All the other points are created dynamically, with point2, point3, and point4 parameterized by the class name. As the creation of point3 (and point4) makes clear, there is no need to use a risky eval() to create instances (as we did for point2). The creation of point4 works exactly the same way as for point3, but using nicer syntax by relying on Python’s built-in globals() function. Point point5 is created using a generic make_object() function that accepts a class object and the relevant arguments. Point point6 is created using the classic prototype approach: first, we clone an existing object, then we initialize or configure it. Point point7 is created by using point point1’s class object, plus new arguments.
Point point6 shows that Python has built-in support for prototyping using the copy.deepcopy() function. However, point7 shows that Python can do better than prototyping: instead of needing to clone an existing object and modify the clone, Python gives us access to any object’s class object, so that we can create a new object directly and much more efficiently than by cloning.