ActiveState Code

Recipe 86651: Prototype pattern


This is an implementation of the prototype pattern. In the prototype pattern you create a new instance cloning from other.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from copy import deepcopy

class Prototype:
    def __init__(self):
        self._objs = {}
        
    def registerObject(self, name, obj):
        """
        register an object.
        """
        self._objs[name] = obj
        
    def unregisterObject(self, name):
        """unregister an object"""
        del self._objs[name]
        
    def clone(self, name, **attr):
        """clone a registered object and add/replace attr"""
        obj = deepcopy(self._objs[name])
        obj.__dict__.update(attr)
        return obj
    

Discussion

class A:pass a=A() prototype=Prototype() prototype.registerObject("a",a) b=prototype.clone("a",a=1,b=2,c=3) b.a 1 b.b 2 b.c 3 When to use Prototype: Example: You have a very complex object that cost too much to instantiate ( a DOM tree or an object that reads a database). Then clone the instance and change attributes as needed.

Comments

  1. 1. At 3:33 a.m. on 10 jan 2002, Hamish Lawson said:

    Should Prototype class be a Singleton or Borg? Am I right in thinking that you would probably only want one instance of the Prototype class in a program? In which case the Prototype class should itself implement the Singleton (or possibly better yet, Borg) pattern.

  2. 2. At 4:14 p.m. on 24 mar 2004, robert stone said:

    Sometimes you may want more than one "prototype manager." What this actually seems to be is an impementation of what the GOF book calls a "prototype manager" with Clone() implemented in the prototype manager rather than the prototype class. This does have its advantages, but anyway...

    You don't necessarily want to have only one single prototype of a given object per application. For example, you might have a (XML) DOM tree object that has its own set of prototypes for various different elements, and another DOM tree object with totally different prototypes for the same elements.

  3. 3. At 11:34 a.m. on 27 mar 2004, Matt Good said:

    Prototype as a metaclass. After looking at http://www.prothon.org/ it seemed like the prototype stuff it does could be acheived through a metaclass. Here's a simple implementation:

    from copy import deepcopy, copy
    
    copyfunc = deepcopy
    
    def Prototype(name, bases, dict):
        class Cls:pass
        Cls.__name__ = name
        Cls.__bases__ = bases
        Cls.__dict__ = dict
        inst = Cls()
        inst.__call__ = copyier(inst)
        return inst
    
    class copyier:
        def __init__(self, inst):
            self._inst = inst
        def __call__(self):
            newinst = copyfunc(self._inst)
            if copyfunc == deepcopy:
                newinst.__call__._inst = newinst
            else:
                newinst.__call__ = copyier(newinst)
        return newinst
    

    You can set 'copyfunc' to 'copy' if you would prefer shallow copies of the prototyped objects and it will still behave appropriately.

    To mimic some of the Prothon examples:

    class Point:
        __metaclass__ = Prototype
        x = 0
        y = 0
        def move(self, x, y):
            self.x += x
            self.y += y
    
    a = Point()
    print a.x, a.y          # prints 0 0
    a.move(100, 100)
    print a.x, a.y          # prints 100 100
    
    Point.move(50, 50)
    print Point.x, Point.y  #prints 50 50
    p = Point()
    print p.x, p.y          #prints 50 50
    
    q = p()
    print q.x, q.y          #prints 50 50
    

    This doesn't support everything that Prothon does. For example, it doesn't currently support __init__ and set_proto (for changing the prototype of an instance). Also, it currently does not support inheritance. If anyone has suggestions for how to support these please post them.

  4. 4. At 9:06 a.m. on 27 oct 2004, robert stone said:

    The "Clone" Protocol. I'm not sure if this deserves its own recipe or not. Since it's strongly related to the Prototype pattern, I thought it might make the most sense to include it here.

    The clone protocol is simple. Basically any object that implements a method clone() that returns a copy of itself implements the clone protocol. The reason for doing things this way is that the user may not know what the copy "deepness" should be for any given object. Implementing the clone protocol allows an object to implement for itself how a copy of itself should be made.

    This protocol can be used with the implementations above. Just replace deepcopy(obj) or copy(obj) with obj.clone().

    You can also define a simple class that provides a default implementation of the clone method, though this isn't really necessary. One of the strengths of python is that you don't have to do this. (An object doesn't have to have a certain type in most cases as long as it implements certain expected methods.)

    from copy import copy
    
    class Cloner(object):
        def clone(self):
            return copy(self)
    

Sign in to comment