Welcome, guest | Sign In | My Account | Store | Cart

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

Python, 21 lines
 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
    

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.

4 comments

Hamish Lawson 19 years, 11 months ago  # | flag

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.

robert stone 17 years, 8 months ago  # | flag

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.

Matt Good 17 years, 8 months ago  # | flag

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.

robert stone 17 years, 1 month ago  # | flag

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)
Created by Andres Tuells on Sat, 3 Nov 2001 (PSF)
Python recipes (4591)
Andres Tuells's recipes (7)

Required Modules

Other Information and Tasks