ActiveState Code

Recipe 286132: Memento design pattern in python


You need to cache instances based on what arguments are passed to them.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class MementoMetaclass(type):    
    cache = {}

    def __call__(self, *args):
        print "="*20
        print "ClassObj:", self
        print "Args:", args
        print "="*20
        cached = self.cache.get(args, None)
        if not cached:
            instance = type.__call__(self, *args)
            self.cache.update({args:instance})
            return instance
        return cached
        
class Foo(object):
    __metaclass__ = MementoMetaclass
    template = ''
    def __init__(self, arg1, arg2, arg3):
        self.template = arg1

a = Foo(1,2,3)
b = Foo(2,3,4)
c = Foo(1,2,3)
d = Foo(2,3,4)
e = Foo(5,6,7)
f = Foo(5,6,7)

print id(a), id(b), id(c), id(d), id(e), id(f)

Discussion

In some situations (maybe with web toolkits) you may need to cache your instances, for example using a web toolkit, once you have an instance of a page you don't need to reinstantiate it every time a user connects.

Using this simple metaclass you will be able to have a cache of instances based on what arguments are passed.

Comments

  1. 1. At 10:37 a.m. on 13 jun 2004, Ian Bicking said:

    Threading. You should be careful with this in a threaded environment. Because of the way it is constructured, two threads may share an instance, but won't necessarily do so. Only with proper locking can you ensure that only one instance will exist for a particular set of parameters.

    If you do not want threads to share instances you must instead implement a pool mechanism, where you fetch objects from a pool, or if the pool has no appropriate instance you create the object. The objects themselves should probably be wrappers in that case, so that you can override __del__ so that it returns the instance to the pool just before it is garbage collected. Alternately, you can insist that any threads that fetch an object explicitly return it to the pool when they are done, or any classes implement a __del__ method themselves.

  2. 2. At 9:03 p.m. on 15 jun 2004, Michele Simionato said:

    You don't really need a metaclass here ... you could just instantiate your instances with a factory function. It would also be clearer to your users. Unless you want to hide the fact that instances are cached and you want your users use the standard instantiation syntax. If this is your goal (for instance because there is a lot of already written code using the standard calling syntax) then the metaclass solution is justified. But I would not use it if I was writing a new application from scratch.

Sign in to comment