Creando un singleton en Python

Esta pregunta no es para la discusión de si el patrón de diseño singleton es deseable, es un anti-patrón, o para cualquier guerra religiosa, pero discutir cómo este patrón se implementa mejor en Python de una manera más pitónica. En este caso, defino 'más pitónico' en el sentido de que sigue el 'principio del menor asombro'.

Tengo varias clases que se convertirían en singletons (mi caso de uso es para un registrador, pero esto no es importante). No deseo saturar varias clases con gumph adicional cuando simplemente puedo heredar o decorar.

Mejores métodos:


Método 1: un decorador

def singleton(class_):
    instances = {}
    def getinstance(*args, **kwargs):
        if class_ not in instances:
            instances[class_] = class_(*args, **kwargs)
        return instances[class_]
    return getinstance

@singleton
class MyClass(BaseClass):
    pass

Ventajas

  • Los decoradores son aditivos de una manera que a menudo es más intuitiva que la herencia múltiple.

Desventajas

  • Si bien los objetos creados con MyClass () serían verdaderos objetos singleton, MyClass en sí es una función, no una clase, por lo que no puede llamar a métodos de clase desde ella. También por
x = MyClass();
y = MyClass();
t = type(n)();

luego x == y pero x != t && y != t


Método 2: una clase base

class Singleton(object):
    _instance = None
    def __new__(class_, *args, **kwargs):
        if not isinstance(class_._instance, class_):
            class_._instance = object.__new__(class_, *args, **kwargs)
        return class_._instance

class MyClass(Singleton, BaseClass):
    pass

Ventajas

  • Es una verdadera clase

Desventajas

  • Herencia múltiple - ¡eugh! __new__ podría sobrescribirse durante la herencia de una segunda clase base? Hay que pensar más de lo necesario.

Método 3: A metaclase

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

#Python2
class MyClass(BaseClass):
    __metaclass__ = Singleton

#Python3
class MyClass(BaseClass, metaclass=Singleton):
    pass

Ventajas

  • Es una verdadera clase
  • Auto-mágicamente cubre la herencia
  • Utiliza materiales de __metaclass__ para su propósito apropiado (y me hizo consciente de ello)

Desventajas

  • ¿Hay alguno?

Método 4: decorador devolviendo una clase con el mismo nombre

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class_, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w,
                                    class_).__new__(class_,
                                                    *args,
                                                    **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(BaseClass):
    pass

Ventajas

  • Es una verdadera clase
  • Auto-mágicamente cubre la herencia

Desventajas

  • ¿No hay una sobrecarga para crear cada nueva clase? Aquí estamos creando dos clases para cada clase que deseamos hacer un singleton. Si bien esto está bien en mi caso, me preocupa que esto no pueda escalar. Por supuesto, existe un tema de debate sobre si es demasiado fácil escalar este patrón ...
  • ¿Cuál es el punto de la _sealed atributo
  • No se pueden llamar métodos del mismo nombre en clases base usando super() porque van a recurrir. Esto significa que no puede personalizar __new__ y no puede subclasificar una clase que necesita que llame a __init__.

Método 5: un módulo

un archivo de módulo singleton.py

Ventajas

  • Lo simple es mejor que lo complejo

Desventajas

  • No perezosamente instanciado

preguntado el 20 de julio de 11 a las 07:07

Otras tres técnicas: use un módulo en su lugar (a menudo, en general, creo que este es un patrón más apropiado para Python, pero depende un poco de lo que esté haciendo con él); hacer una sola instancia y tratar con ella en su lugar (foo.x o si insistes Foo.x en lugar de Foo().x); usar atributos de clase y métodos estáticos / de clase (Foo.x). -

@ChrisMorgan: Si vas a usar métodos de clase / estáticos solamente, entonces no te molestes en hacer una clase, de verdad. -

@Cat: El efecto es similar, sin embargo, las razones detrás de la creación de una variable global pueden ser casi cualquier cosa, incluso no saber nada mejor. ¿Por qué se crea un singleton? Si tienes que preguntar, no deberías estar aquí. Esta explicitación no solo es más pitónica, sino que hace que el mantenimiento sea mucho más simple. Sí, los singleton son azúcar sintáctico para los globales, pero las clases son azúcar sintáctico para un montón de cosas desagradables y no creo que nadie te diga que siempre estás mejor sin ellos. -

El sentimiento anti-signletons es la programación de culto de carga en su peor momento. Lo mismo ocurre con las personas que escuchan (pocos se molestan en leer) "La declaración de Goto se considera dañina" y piensan que los gotos son un signo de código incorrecto, independientemente del contexto. -

0 Respuestas

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.