Singleton: algun standard?

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:

  1. hay algo standard ya en python? (o alguna lib de 3ros que ya sea muy popular?)

  2. 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 :slight_smile:

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.