Hola!
Hay algun “singleton” standard en python?
Me gustaba usar la clase (directamente) como singleton, como si fuera una instancia, metiendo classmethods… funciona mas o menos, pero un problema es no poder ponerle cualquier “protocolo” tipo: [gs]etitem …
La otra opcion comun es tener una variable de clase conteniendo una instancia, y un class method para accederla o crearla si no se creo antes.
class LoQueSea:
_instance = None
def __init__():
# solo a ser llamado por mi mismo
...
@classmethod
def Get(cls):
if cls._instance is None:
cls._instance = cls()
return cls._instance
Pero las dudas son:
-
hay algo standard ya en python? (o alguna lib de 3ros que ya sea muy popular?)
-
que solucion soporta o como adaptan esto para que soporte estar definido en un modulo y que el modulo sea recargado? (tengo mi hack asqueroso por ahi, pero quisiera ver como se deberia hacer)
Saludos y gracias x el nuevo sitio y nuevo foro, ambos!
dave
Hola Tenuki!
A nivel “singleton standard” lo que se recomienda utilizar es un módulo. Tiene la propiedad de que lo importes N veces y siempre tenés el mismo.
Claro, si alguien te “recarga a mano” el módulo, perdiste esa cualidad.
Lo otro que se hace es toquetear el __new__
de las clases para que te devuelvan siempre la misma instancia, entonces la instanciás N veces pero realmente tenés una sola instancia:
>>> class C:
...: _instance = None
...: def __new__(cls, *args, **kwargs):
...: if cls._instance is None:
...: cls._instance = object.__new__(cls)
...: return cls._instance
...:
...:
>>>
>>> c1 = C()
>>> c2 = C()
>>> c1 is c2
True
Esto tampoco es inmune a que te recarguen el módulo. Pero es trivial, en vez de guardar el “cache de la instancia” en cls._instance
, la podés guardar en __builtins__
y ya.
Pero claro, tampoco es inmune a si corrés dos procesos de Python 
2 Me gusta
Perdon, me exprese mal… esos dos ejemplos que di, no andan en caso de “recarga” de modulo… y lo que me paso, no fue a mano, sino usando la libreria para hacer bots para telegram.
Si, lo del __new__
es una variacion piola del segundo ejemplo que di, esta buena. Pero sufre de lo mismo.
Ahh… lo de __builtins__
se me paso. Habia intentado utilizar globals()
y sys.modules
sin exito… Despues voy a probar lo de __builtins__
.
Gracias!
2 Me gusta
Yo hace mucho que uso esto, para cosas chiquitas, no sé qué tan robusto es.
'''El módulo que expone LaClase'''
class LaClase:
def elmetodo(self):
pass
LaClase = LaClase()
'''El código que usa LaClase'''
from laclase import LaClase
LaClase.elmetodo()
Qué desventajas le ven?
2 Me gusta
Me sumo a la pregunta de mmodenesi, yo en mis ultimos dos proyectos estoy usando el mismo método.
Me gusta su simpleza, pero tambien tengo dudas de si es la mejor forma de implementar singleton
1 me gusta
Me parece que primero hay una cuestion de gustos y comodidad.
Las contras que le veo a ese metodo es que si uno dice LaClase en un momento se refiere a una clase y luego a una instancia (aunque mayor parte del tiempo va a ser la instancia). Y por ahi es confuso o puede confundir a un IDE o analizador de codigo, tambien.
Desde ya, tambien, me parece que no debe soportar “reload( laclase )” … es decir… si te importaron el modulo laclase, alguien se queda con una referencia a LaClase y otra parte del codigo hace reload(…) generaria una nueva instancia de LaClase en el nuevo modulo generado por el reload… con lo cual dejaria de ser singleton… (ojo, como decia Facu seria raro … no es muy buena practica que alguien haga reload(…)… pero bueno… a veces no depende de uno…).
Saludos!
Entiendo tampoco me gusta eso, uso alias a la hora de importar, copiando el ejemplo anterior
'''El código que usa LaClase'''
from laclase import LaClase as laclase
laclase.elmetodo()
Si tenía en cuenta lo del reload, y veo tambien que es una cuestión mas de gustos.
1 me gusta
Gracias por señalar lo del reload
, no lo había pensado.
Lo de clase vs instancia, personalmente, no me molesta. Justo este ejemplo pone en primer plano la inconsistencia por el nombre (tonto nombre) de la clase.
Pero si mi código hace
from printer import PrinterQueue
job = PrinterQueue.add_job(...)
...
no me hace mucho ruido.