Comprobando la existencia de miembros en Python

Regularmente quiero comprobar si un objeto tiene un miembro o no. Un ejemplo es la creación de un singleton en una función. Para ese propósito, puedes usar hasattr así:

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

Pero también puedes 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 que el otro?

Edición: se agregó @classmethod … Pero tenga en cuenta que la pregunta no es sobre cómo hacer un singleton sino cómo verificar la presencia de un miembro en un objeto.

Edición: para ese ejemplo, un uso típico sería:

 s = Foo.singleton() 

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

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

Los pitonistas suelen sugerir que EAFP es mejor, con argumentos en estilo de “¿qué sucede 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 deben ser tratadas como demasiado excepcionales.

El rendimiento en su caso: desde que configurar administradores de excepciones (la palabra clave try ) es muy barato en CPython, mientras que crear una excepción (la palabra clave raise y la creación de excepciones internas) es lo que es relativamente costoso: utilizando el método №2, se plantearía la excepción sólo una vez; Después, solo usas la propiedad.

Solo 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. A mi parecer, también me resulta más legible, de todos modos, según el caso, esta prueba fue muy simple, tal vez necesitaría una más compleja.

Depende de qué caso sea “típico”, porque las excepciones deben modelar, bueno, las condiciones atípicas. Entonces, si el caso típico es que el atributo de instance debe existir, entonces use el segundo estilo de código. Si no tener 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 crear un singleton la hora inicial es un caso de uso típico. 🙂

Un poco fuera de tema en la forma de usarlo. Los Singletons están sobrevalorados, y un método de “estado compartido” es igual de efectivo y, en su mayoría, 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 haces:

 obj = Borg() 

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

Tengo que estar de acuerdo con Chris. Recuerde, no optimice hasta que realmente necesite hacerlo. Realmente dudo que verificar la existencia vaya a ser un cuello de botella en cualquier progtwig razonable.

También vi http://code.activestate.com/recipes/52558/ como una forma de hacer esto. Copia sin comentar de ese código (“spam” es solo un método aleatorio que tiene la interfaz de clase):

 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)