2009-03-21 7 views
2

J'essaie de créer une classe qui ne recréera pas un objet avec les mêmes paramètres d'entrée. Lorsque j'essaie d'instancier une classe avec les mêmes paramètres que ceux utilisés pour créer un objet déjà existant, je veux juste que ma nouvelle classe retourne un pointeur vers l'objet déjà créé (créé de manière coûteuse). Voilà ce que je l'ai essayé jusqu'à présent:Comment créer une classe qui ne recrée pas un objet avec des paramètres d'entrée identiques

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' 

Ceci est ma première incursion dans __new__ primordiale et je suis convaincu que je ne vais pas parler de la bonne façon. Réglez-moi directement, s'il vous plaît.

Répondre

8

D'abord, utilisez des noms de classes majuscules en Python.

Ensuite, utilisez un motif de motif Factory pour résoudre ce problème.

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] 

Ceci est beaucoup plus simple que de jouer avec de nouveaux groupes d'objets de niveau classe.

+0

C'est exactement la meilleure façon de le faire. Parfaitement suggéré. – mpeterson

+1

L'utilisation de '__new__' serait en effet la façon/pythonique de le faire plutôt que d'introduire une autre classe - votre solution est Java idiomatique. N'insérez pas d'espaces entre parenthèses et n'utilisez pas les noms de méthode camelCase dans Python. –

11

est ici un décorateur de classe pour faire une classe un multiton:

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

(Ceci est une légère variante du décorateur singleton du PEP 318.)

Ensuite, pour rendre votre classe un multiton, utiliser le décorateur:

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

maintenant, si vous instancier MyObject avec le même identifiant, vous obtenez la même instance:

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

a is b # False 
b is c # True 
+0

C'est de loin une solution plus propre et tout aussi efficace et simple que celle suggérée par S.Lott. –

+0

@ErikAllik, vous écrivez, "Utiliser' __new__' serait en effet la façon/pythonic de le faire. " Préférez-vous la solution de @ Jerry à celle qui utilise '__new__'? – kuzzooroo

+0

@kuzzooroo: Je dirais que oui: la solution de Jerry est moins magique et plus directe, et Pythonic en même temps. –

Questions connexes