2008-10-15 23 views
23

Regularmente quiero comprobar si un objeto tiene un miembro o no. Un ejemplo es la creación de un singleton en una función. A tal fin, se puede utilizar hasattr así:Comprobando la existencia del miembro en Python

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

Pero también se puede hacer esto:

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

es un método mejor de la otra?

Editar: Agregado el @classmethod ... Pero tenga en cuenta que la cuestión es no acerca de cómo hacer un producto único, pero cómo comprobar la presencia de un miembro de un objeto.

Editar: Para ese ejemplo, un uso típico sería:

s = Foo.singleton() 

Entonces s es un objeto de tipo Foo, la misma cada vez. Y, típicamente, el método se llama muchas veces.

+0

¿Puede dar un ejemplo de una llamada que está tratando de hacer, y el retorno esperado de esa llamada? – Baltimark

Respuesta

21

Estos son dos metodologías diferentes: №1 es LBYL (mirar antes de saltar) y №2 es EAFP (más fácil pedir perdón que permiso).

Los Pythonistas suelen sugerir que EAFP es mejor, con argumentos en el estilo de "¿Y si un proceso crea el archivo entre el momento en que lo prueba y el momento en que intenta crearlo usted mismo?". Este argumento no se aplica aquí, pero es la idea general. Las excepciones no se deben tratar como demasiado excepcional.

En cuanto al rendimiento, en su caso -desde la creación de gestores de excepción (la try de palabras clave) es muy barato en CPython mientras se crea una excepción (la palabra clave y la creación raise excepción interna) es lo que es relativamente caro-utilizando el método de la №2 la excepción se plantearía solo una vez; luego, solo usa la propiedad.

5

Depende de qué caso es "típico", porque las excepciones deben modelar, bueno, las condiciones atípicas. Entonces, si el caso típico es que el atributo instance debería existir, entonces use el segundo estilo de código. Si no tiene instance es tan típico como tener instance, entonces use el primer estilo.

En el caso específico de crear un singleton, me inclino por ir con el primer estilo, porque la creación de un singleton es un caso de uso típico. :-)

10

Sólo traté de medir los tiempos:

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 

En mi máquina:

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 

Parece que try/except es más rápido. También me parece más legible, de todos modos depende del caso, esta prueba fue muy simple, tal vez necesitaría una más compleja.

+0

Me pregunto si estos resultados de velocidad son un poco engañosos (¡pero siguen siendo realmente interesantes!) Porque hasattr usa excepciones en segundo plano para determinar si existe el attr – seans

0

Tengo que estar de acuerdo con Chris. Recuerde, no optimice hasta que realmente necesite hacerlo. Realmente dudo que verifique la existencia sea un cuello de botella en cualquier programa razonable.

Vi http://code.activestate.com/recipes/52558/ como una forma de hacer esto, también. copia sin comentar que el código ("spam" es sólo un método aleatorio de la interfaz de clase tiene):

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

y cuál es el punto de la línea 'self .__ dict __ ['_ Singleton__instance'] = Singleton .__ instancia'? esto debería ser equivalente a self .__ instance = Singleton .__ instancia, ¿no es así? – PierreBdR

1

Un poco fuera de tema en la forma de usarlo. Singleton están sobrevalorados, y un método de "estado compartido" es tan eficaz, y sobre todo, muy limpio en Python, por ejemplo:

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

Ahora, cada vez que lo haga:

obj = Borg() 

tendrá la misma información, o, ser de alguna manera la misma instancia.

Cuestiones relacionadas