2

我正在尝试创建一个不会重新创建具有相同输入参数的对象的类。当我尝试使用用于创建已存在对象的相同参数来实例化一个类时,我只希望我的新类返回一个指向已创建(昂贵创建)对象的指针。这是我到目前为止所尝试的:

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__,我确信我不会以正确的方式去做。请让我直截了当。

4

3 回答 3

15

这是一个类装饰器,可以使一个类成为 multiton:

def multiton(cls):
   instances = {}
   def getinstance(id):
      if id not in instances:
         instances[id] = cls(id)
      return instances[id]  
   return getinstance

(这是 PEP 318 中单例装饰器的一个轻微变体。)

然后,为了让你的类成为一个 multiton,使用装饰器:

@multiton
class MyObject( object ):
   def __init__( self, arg):
      self.id = arg
      # other expensive stuff

现在,如果您使用相同的 id 实例化 MyObject,您将获得相同的实例:

a = MyObject(1)
b = MyObject(2)
c = MyObject(2)

a is b  # False
b is c  # True
于 2011-09-21T01:27:16.197 回答
7

首先,在 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]

这比玩弄新的和拥有类级别的对象池要简单得多。

于 2009-03-21T20:31:15.173 回答
1

这是我对 Jerry 方式的实现,使用数组作为池

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])
于 2021-07-09T08:57:52.230 回答