Reputation: 43650
I am trying to create a class that doesn't re-create an object with the same input parameters. When I try to instantiate a class with the same parameters that were used to create an already-existing object, I just want my new class to return a pointer to the already-created (expensively-created) object. This is what I have tried so far:
class myobject0(object):
# At first, I didn't realize that even already-instantiated
# objects had their __init__ called again
instances = {}
def __new__(cls,x):
if x not in cls.instances.keys():
cls.instances[x] = object.__new__(cls,x)
return cls.instances[x]
def __init__(self,x):
print 'doing something expensive'
class myobject1(object):
# I tried to override the existing object's __init__
# but it didnt work.
instances = {}
def __new__(cls,x):
if x not in cls.instances.keys():
cls.instances[x] = object.__new__(cls,x)
else:
cls.instances[x].__init__ = lambda x: None
return cls.instances[x]
def __init__(self,x):
print 'doing something expensive'
class myobject2(object):
# does what I want but is ugly
instances = {}
def __new__(cls,x):
if x not in cls.instances.keys():
cls.instances[x] = object.__new__(cls,x)
cls.instances[x]._is_new = 1
else:
cls.instances[x]._is_new = 0
return cls.instances[x]
def __init__(self,x):
if self._is_new:
print 'doing something expensive'
This is my first venture into overriding __new__
and I'm convinced I'm not going about it the right way. Set me straight, please.
Upvotes: 4
Views: 2747
Reputation: 91
Here is my implementation of Jerry's way, using an array as the pool
def pooled(cls):
"""
decorator to add to a class, so that when you call YourClass() it actually returns an object from the pool
"""
pool = []
def get_instance(*args, **kwargs):
try:
instance = pool.pop()
except IndexError:
instance = cls(*args, **kwargs)
returned_instance = yield instance
pool.append(returned_instance)
print(len(pool))
yield
return get_instance
@pooled
class MyClass():
def __init__(self, num):
self.num = num
for i in range(10):
m_gen =MyClass(i)
n_gen = MyClass(i + 5)
m = next(m_gen)
n = next(n_gen)
print(f'm num: {m.num}')
print(f'n num: {n.num}')
m_gen.send(m)
n_gen.send(n)
and then another way using metaclasses so you can inherit the functionality. This one uses weakreaf valuedicts as the pool, so the objects get garbage collected better
import weakref
class PooledMeta(type):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._pool = weakref.WeakValueDictionary()
def __call__(self, *args):
if args in self._pool:
print('got cached')
return self._pool[args]
else:
# print(self._pool.valuerefs())
instance = super().__call__(*args)
self._pool[args] = instance
return instance
class MyPooled(metaclass=PooledMeta):
def __init__(self, num):
print(f'crating: {num}')
self.num = num
class MyPooledChild(MyPooled):
def __init__(self, num):
print(f'crating child: {num}')
self.num = num
p = []
for i in range(10):
m = MyPooled(i)
n = MyPooledChild(i)
p.extend([m,n])
Upvotes: 1
Reputation: 425
Here's a class decorator to make a class a multiton:
def multiton(cls):
instances = {}
def getinstance(id):
if id not in instances:
instances[id] = cls(id)
return instances[id]
return getinstance
(This is a slight variant of the singleton decorator from PEP 318.)
Then, to make your class a multiton, use the decorator:
@multiton
class MyObject( object ):
def __init__( self, arg):
self.id = arg
# other expensive stuff
Now, if you instantiate MyObject with the same id, you get the same instance:
a = MyObject(1)
b = MyObject(2)
c = MyObject(2)
a is b # False
b is c # True
Upvotes: 16
Reputation: 391952
First, use Upper Case Class Names in Python.
Second, use a Factory design pattern to solve this problem.
class MyObject( object ):
def __init__( self, args ):
pass # Something Expensive
class MyObjectFactory( object ):
def __init__( self ):
self.pool = {}
def makeMyObject( self, args ):
if args not in self.pool:
self.pool[args] = MyObject( args )
return self.pool[args]
This is much simpler than fooling around with new and having class level pools of objects.
Upvotes: 9