2009-10-21 7 views
6

ma classe Python a des variables qui requièrent du travail pour calculer la première fois qu'elles sont appelées. Les appels suivants devraient simplement renvoyer la valeur précalculée.Façon pythonienne de ne fonctionner que la première fois qu'une variable est appelée

Je ne veux pas perdre de temps à faire ce travail à moins qu'ils ne soient réellement nécessaires à l'utilisateur. Y a-t-il un moyen propre de Pythonic pour implémenter ce cas d'utilisation?

Ma première pensée était d'utiliser la propriété() pour appeler une fonction pour la première fois, puis remplacer la variable:

class myclass(object): 
    def get_age(self): 
     self.age = 21 # raise an AttributeError here 
     return self.age 

    age = property(get_age) 

Merci

+2

Demandez-vous à propos de la mémo? http://en.wikipedia.org/wiki/Memoization. C'est un modèle de conception OO assez commun. –

Répondre

13
class myclass(object): 
    def __init__(self): 
     self.__age=None 
    @property 
    def age(self): 
     if self.__age is None: 
      self.__age=21 #This can be a long computation 
     return self.__age 

Alex dit que vous pouvez utiliser __getattr__, c'est comment cela fonctionne

class myclass(object): 
    def __getattr__(self, attr): 
     if attr=="age": 
      self.age=21 #This can be a long computation 
     return super(myclass, self).__getattribute__(attr) 

__getattr__() est invoquée lorsqu'au hommage n'existe pas sur l'objet, à savoir. la première fois que vous essayez d'accéder à age. Chaque fois après, age existe donc __getattr__ ne soit pas appelé

+0

merci - __getattr __() fonctionne mieux pour moi – hoju

+1

l'appel 'return getattr (self, attr)' provoquerait une boucle infinie si attr n'existe pas. Utilisez 'return super (MyClass, self) .__ getattr __ (attr)' à la place. – nosklo

+0

Correction, merci nosklo –

6

property, comme vous » vu, ne vous laissera pas le contourner. Vous devez utiliser une approche légèrement différente, par exemple:

class myclass(object): 

    @property 
    def age(self): 
     if not hasattr(self, '_age'): 
     self._age = self._big_long_computation() 
     return self._age 

Il existe d'autres approches telles que __getattr__ ou une classe de descripteur personnalisé, mais celui-ci est plus simple -)

+1

huh, c'est une erreur de syntaxe? – nosklo

+0

@nosklo, oups, fait trop de C dernièrement - corrigé, tx –

4

Here est décorateur de Python Cookbook! pour ce problème:

class CachedAttribute(object): 
    ''' Computes attribute value and caches it in the instance. ''' 
    def __init__(self, method, name=None): 
     # record the unbound-method and the name 
     self.method = method 
     self.name = name or method.__name__ 
    def __get__(self, inst, cls): 
     if inst is None: 
      # instance attribute accessed on class, return self 
      return self 
     # compute, cache and return the instance's attribute value 
     result = self.method(inst) 
     setattr(inst, self.name, result) 
     return result 
Questions connexes