Как создать класс, который не воссоздает объект с одинаковыми входными параметрами
Я пытаюсь создать класс, который не воссоздает объект с теми же входными параметрами. Когда я пытаюсь создать экземпляр класса с теми же параметрами, которые использовались для создания уже существующего объекта, я просто хочу, чтобы мой новый класс возвращал указатель на уже созданный (дорогостоящий) объект. Это то, что я пробовал до сих пор:
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'
Это моя первая попытка переопределить __new__
и я убежден, что не пойду правильно. Поправьте меня, пожалуйста.
3 ответа
Во-первых, используйте имена классов верхнего регистра в Python.
Во-вторых, используйте шаблон фабричного дизайна, чтобы решить эту проблему.
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]
Это намного проще, чем дурачиться с новыми и имеющими пулы уровня класса объектов.
Вот декоратор класса, чтобы сделать класс многотонным:
def multiton(cls):
instances = {}
def getinstance(id):
if id not in instances:
instances[id] = cls(id)
return instances[id]
return getinstance
(Это небольшой вариант одиночного декоратора из PEP 318.)
Затем, чтобы сделать ваш класс многотонным, используйте декоратор:
@multiton
class MyObject( object ):
def __init__( self, arg):
self.id = arg
# other expensive stuff
Теперь, если вы создаете экземпляр MyObject с тем же идентификатором, вы получаете тот же экземпляр:
a = MyObject(1)
b = MyObject(2)
c = MyObject(2)
a is b # False
b is c # True
Вот моя реализация пути Джерри с использованием массива в качестве пула
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)
а затем другой способ использования метаклассов, чтобы вы могли унаследовать функциональность. В этом случае в качестве пула используются функции weakreaf valuedicts, поэтому объекты лучше собирают мусор.
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])