2008-10-15 5 views
23

Je souhaite régulièrement vérifier si un objet a un membre ou non. Un exemple est la création d'un singleton dans une fonction. A cet effet, vous pouvez utiliser hasattr comme ceci:Vérification de l'existence d'un membre en Python

class Foo(object): 
    @classmethod 
    def singleton(self): 
     if not hasattr(self, 'instance'): 
      self.instance = Foo() 
     return self.instance 

Mais vous pouvez aussi le faire:

class Foo(object): 
    @classmethod 
    def singleton(self): 
     try: 
      return self.instance 
     except AttributeError: 
      self.instance = Foo() 
      return self.instance 

est une méthode meilleure de l'autre?

Edit: Ajouté le @classmethod ... Mais notez que la question est pas sur la façon de faire un singleton, mais comment vérifier la présence d'un membre dans un objet.

Edit: Pour cet exemple, une utilisation typique serait:

s = Foo.singleton() 

Alors s est un objet de type Foo, le même à chaque fois. Et, typiquement, la méthode est appelée plusieurs fois.

+0

Pouvez-vous donner un exemple d'appel que vous essayez d'effectuer et le rendement attendu de cet appel? – Baltimark

Répondre

21

Ce sont deux méthodes différentes: №1 est LBYL (regarder avant de sauter) et №2 est EAFP (plus facile demander pardon que la permission). Les Pythonistas suggèrent généralement que EAFP est meilleur, avec des arguments dans le style de "et si un processus crée le fichier entre le moment où vous le tester et le moment où vous essayez de le créer vous-même?". Cet argument ne s'applique pas ici, mais c'est l'idée générale. Les exceptions ne doivent pas être traitées comme trop exceptionnelles.

Côté performance dans votre cas -Depuis la mise en place des gestionnaires d'exception (le mot-clé try) est très pas cher à CPython tout en créant une exception (le mot-clé raise et la création d'exception interne) est ce qui est relativement expensive- en utilisant la méthode №2 la l'exception ne serait soulevée qu'une seule fois; après, vous utilisez simplement la propriété.

5

Cela dépend de quel cas est "typique", car les exceptions doivent modéliser, bien, des conditions atypiques. Donc, si le cas typique est que l'attribut instance devrait exister, alors utilisez le second style de code. Si vous n'avez pas instance est aussi typique que d'avoir instance, alors utilisez le premier style.

Dans le cas spécifique de la création d'un singleton, je suis enclin à utiliser le premier style, car la création d'un singleton l'heure initiale est un cas d'utilisation typique. :-)

10

Je viens d'essayer de mesurer les temps:

class Foo(object): 
    @classmethod 
    def singleton(self): 
     if not hasattr(self, 'instance'): 
      self.instance = Foo() 
     return self.instance 



class Bar(object): 
    @classmethod 
    def singleton(self): 
     try: 
      return self.instance 
     except AttributeError: 
      self.instance = Bar() 
      return self.instance 



from time import time 

n = 1000000 
foo = [Foo() for i in xrange(0,n)] 
bar = [Bar() for i in xrange(0,n)] 

print "Objs created." 
print 


for times in xrange(1,4): 
    t = time() 
    for d in foo: d.singleton() 
    print "#%d Foo pass in %f" % (times, time()-t) 

    t = time() 
    for d in bar: d.singleton() 
    print "#%d Bar pass in %f" % (times, time()-t) 

    print 

Sur ma machine:

Objs created. 

#1 Foo pass in 1.719000 
#1 Bar pass in 1.140000 

#2 Foo pass in 1.750000 
#2 Bar pass in 1.187000 

#3 Foo pass in 1.797000 
#3 Bar pass in 1.203000 

Il semble que try/except est plus rapide. Cela semble aussi plus lisible pour moi, de toute façon cela dépend du cas, ce test était très simple peut-être que vous en auriez besoin d'un plus complexe.

+0

Je me demande si ces résultats de vitesse sont légèrement trompeurs (mais toujours très intéressants!) Car hasattr utilise des exceptions en arrière-plan pour déterminer si l'attr existe – seans

0

Je suis d'accord avec Chris. Rappelez-vous, n'optimisez pas jusqu'à ce que vous ayez réellement besoin de le faire. Je doute vraiment de vérifier l'existence va être un goulot d'étranglement dans tout programme raisonnable. J'ai également vu http://code.activestate.com/recipes/52558/ pour ce faire. copie décommentée de ce code (« spam » est juste une méthode aléatoire l'interface de classe a):

class Singleton: 
    class __impl: 
     def spam(self): 
      return id(self) 
    __instance = None 
    def __init__(self): 
     if Singleton.__instance is None: 
      Singleton.__instance = Singleton.__impl() 
     self.__dict__['_Singleton__instance'] = Singleton.__instance 
    def __getattr__(self, attr): 
     return getattr(self.__instance, attr) 
    def __setattr__(self, attr, value): 
     return setattr(self.__instance, attr, value) 
+0

Et quel est le point de la ligne 'self .__ dict __ ['_ Singleton__instance'] = Singleton .__ instance'? cela devrait être équivalent à self .__ instance = Singleton .__ instance, n'est-ce pas? – PierreBdR

1

Un peu hors sujet dans la façon de l'utiliser. Singletons sont surévaluées, et une méthode « à l'état partagé » est aussi efficace, et surtout, très propre en python, par exemple:

class Borg: 
    __shared_state = {} 
    def __init__(self): 
     self.__dict__ = self.__shared_state 
    # and whatever else you want in your class -- that's all! 

Maintenant, chaque fois que vous faites:

obj = Borg() 

il aura la même information, ou, être un peu la même instance.

Questions connexes