¿Cuándo usar un Singleton en python?

Hay muchas preguntas relacionadas con el uso del patrón Singleton en python, y aunque esta pregunta podría repetir muchos de los aspectos ya discutidos, no he encontrado la respuesta a la siguiente pregunta específica.

Supongamos que tengo una clase MyClass que quiero instanciar solo una vez exactamente. En Python puedo hacer esto de la siguiente manera en el código myclass.py :

 class MyClass(object): def foo(self): .... instance = MyClass() 

Entonces en cualquier otro progtwig puedo referirme a la instancia simplemente con

 import myclass myclass.instance.foo() 

¿Bajo qué circunstancias es suficiente este enfoque? ¿Bajo qué circunstancias es útil / obligatorio el uso de un patrón Singleton?

El patrón de singleton es más a menudo una cuestión de conveniencia que de requisito. Python es un poco diferente a otros idiomas en que es bastante fácil burlarse de los singletons en las pruebas (¡solo tienes que golpear la variable global!) En comparación con otros idiomas, pero no es una buena idea preguntarte a ti mismo al crear un singleton: ¿Estoy haciendo esto por conveniencia o porque es estrictamente necesario que solo haya una instancia? ¿Es posible que haya más de uno en el futuro?

Si creas una clase que realmente solo se construirá una vez, puede que tenga más sentido hacer que el estado sea parte del módulo y que sus métodos se conviertan en funciones de nivel de módulo. Si existe la posibilidad de que la suposición de exactamente una instancia pueda cambiar en el futuro, a menudo es mucho mejor pasar la instancia de singleton en lugar de hacer referencia al singleton a través de un nombre global.

Por ejemplo, puede implementar fácilmente un “singleton” de esta manera:

 if __name__ == '__main__': instance = MyClass() doSomethingWith(instance) 

En lo anterior, “instancia” es singleton en virtud del hecho de que se construye solo una vez, pero el código que lo maneja se proporciona en lugar de hacer referencia a module.instance , lo que facilita la reutilización de partes del código si, en alguna situación futura, necesita más de una MyClass .

Suponiendo que desea utilizar un módulo como un singleton como sugiere Michael Aaron Safyan, puede hacerlo funcionar incluso si el código principal no importa el módulo haciendo algo como lo siguiente (en el código principal o en un módulo que sí importa) directa o indirectamente). Lo que hace es hacer que un atributo de clase de instance inicialice en uno y luego reemplace el objeto de módulo en sys.modules con la instancia creada:

 class _MyClass(object): def foo(self): print 'foo()' _MyClass.instance = _MyClass() import sys _ref = sys.modules[__name__] # Reference to current module so it's not deleted sys.modules[__name__] = _MyClass.instance 

He encontrado singleton una forma útil de implementar “registros” de cosas cuando tiene sentido tener solo uno (registro), como un grupo de clases para una fábrica de clases, un grupo de constantes o un conjunto de información de configuración . En muchos casos, solo un módulo regular de Python funcionará bien porque, de forma predeterminada , ya están efectivamente singletons debido a que los que ya están cargados se almacenan en caché en el diccionario sys.modules .

Sin embargo, de vez en cuando, las instancias de clase son preferibles porque se pueden pasar parámetros de construcción y tener propiedades , algo que los objetos de módulos incorporados no pueden y no pueden hacerse poseer. Las limitaciones de este tipo pueden solucionarse utilizando el truco que se muestra arriba, que convierte efectivamente las instancias de clase personalizadas en objetos de módulo.

La idea de usar instancias de clase como objetos de módulos es de la receta ActiveState de Alex Martelli llamada Constantes en Python .

En mi humilde opinión, hay dos lados en el patrón de singleton.

  • desea un contexto único para un servicio dado porque más de uno no tiene sentido.
  • desea evitar absolutamente que las personas creen dos objetos de un tipo dado porque podría interrumpir su servicio

Mientras que el primer caso puede tener algunas aplicaciones (servicio de registro), el segundo es a menudo el signo de un mal diseño.

Debe diseñar su API para que sus usuarios no tengan que pensar en este problema. Pero si cavan a través de sus capas indocumentadas para encontrar su constructor oculto y desean usarlo por cualquier razón, no deberían tener que lidiar con construcciones inútiles creadas para evitar que hagan lo que tienen que hacer.