You need to cache instances based on what arguments are passed to them.
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)
|
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.
Tags: oop
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.
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.
This is a sweet pattern for cached classes. I would recommend, however, using a cache key that also includes the class involved, so that MementoMetaclass can safely be the metaclass for multiple classes simultaneously. I've also taken the liberty of reorganizing the code to improve the ordering and use the
try: ... except KeyError: ...
idiom currently in vogue.After using
MementoMetaclass
in several projects, I extended it to support keyword arguments. This exposes some possible complications (see comments in code), but in practice has not been a problem.