¿Existe una manera simple y elegante de definir los singletons? [duplicate]

11 minutos de lectura

1646746516 437 ¿Existe una manera simple y elegante de definir los singletons
Jaime

Parece que hay muchas maneras de definir solteros en Python. ¿Existe una opinión consensuada sobre Stack Overflow?

  • Los solteros son mentirosos patológicos¿no es así?

    – Jonas Byström

    5 de julio de 2012 a las 15:01

  • “Esta pregunta no se ajusta bien a nuestro formato de preguntas y respuestas”: creo que esta no es una pregunta subjetiva, ¿hay alguna manera de hacer preguntas de tal manera que se ajusten al formato SO de preguntas y respuestas?

    – Binithb

    13 de septiembre de 2014 a las 22:13


  • No estoy de acuerdo en que esto no sea constructivo. ¿Podría reabrirse si se traslada a programadores.stackexchange.com ?

    – langlauf.io

    6 de agosto de 2015 a las 9:35


  • @stackoverflowwww no porque está basado en opiniones y progs.SE no le gusta eso.

    – monstruo de trinquete

    6 de agosto de 2015 a las 9:44

  • @ratchetfreak Lo que hace que la pregunta sea popular es que las personas como yo buscan diferentes formas de crear Singletons en python. Existen alternativas con pros y contras o que pueden ser adecuadas solo en determinadas situaciones. La pregunta podría reformularse como “¿Qué formas diferentes existen en Python para crear un singleton? Estoy especialmente interesado en la diferencia entre las soluciones que se basan en una clase y las que se basan en una instancia de clase”.

    – langlauf.io

    6 de agosto de 2015 a las 9:49


1646746516 846 ¿Existe una manera simple y elegante de definir los singletons
Staale

Realmente no veo la necesidad, ya que un módulo con funciones (y no una clase) serviría bien como singleton. Todas sus variables estarían vinculadas al módulo, que de todos modos no se podría instanciar repetidamente.

Si desea usar una clase, no hay forma de crear clases privadas o constructores privados en Python, por lo que no puede protegerse contra instancias múltiples, aparte de la convención en el uso de su API. Todavía pondría métodos en un módulo y consideraría el módulo como el singleton.

  • ¿No podría el constructor simplemente verificar si ya se ha creado una instancia y lanzar una excepción si lo ha sido?

    – Casebash

    16 de mayo de 2010 a las 7:56

  • Esto está bien siempre que no necesite usar la herencia como parte de su diseño, en cuyo caso la mayoría de las respuestas a continuación son más adecuadas

    –Jim Jeffries

    6 de junio de 2011 a las 8:23

  • Está roto cuando tienes importación cíclica.

    – fin de semana

    25 de septiembre de 2012 a las 10:49

  • ¿Qué haré si quiero que ese módulo sea heredable?

    – yossi

    26 de noviembre de 2012 a las 9:09

  • Esto es falso en mi opinión. Una molestia sobre las interfaces de nivel de módulo es la gestión de las importaciones. Por ejemplo, Pitón logging es una interfaz de nivel de módulo. Para asegurarse de que limpia completamente después logging debes llamar logging.shutdown(). Esto significa que debe importar logging en el módulo que llama shutdown. Si fuera un patrón singleton, es posible llamar al apagado de la instancia en cualquier módulo al que se pase.

    – Mate

    25 de febrero de 2016 a las 6:49

1646746517 635 ¿Existe una manera simple y elegante de definir los singletons
Pablo Manta

Aquí está mi propia implementación de singletons. Todo lo que tienes que hacer es decorar la clase; para obtener el singleton, entonces tienes que usar el Instance método. Aquí hay un ejemplo:

@Singleton
class Foo:
   def __init__(self):
       print 'Foo created'

f = Foo() # Error, this isn't how you get the instance of a singleton

f = Foo.instance() # Good. Being explicit is in line with the Python Zen
g = Foo.instance() # Returns already created instance

print f is g # True

Y aquí está el código:

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Also, the decorated class cannot be
    inherited from. Other than that, there are no restrictions that apply
    to the decorated class.

    To get the singleton instance, use the `instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)

  • Python con batería incluida, esto debería ser parte de un desing_pattern biblioteca estándar, gracias

    – deslumbrante

    31 de agosto de 2014 a las 0:23

  • @akhan Decidí no admitir constructores con argumentos a propósito, porque los argumentos solo se usarían la primera vez y se ignorarían todas las demás veces. Esto puede hacer que su código sea muy difícil de seguir, ya que puede usar diferentes argumentos en diferentes lugares, pero es posible que no sepa cuál de estas llamadas es la que realmente inicializa el singleton.

    – Pablo Manta

    28 de abril de 2017 a las 7:53

  • @akhan Si realmente desea inicializar su singleton con argumentos, debe tener un separado initialize() método que puede tomar cualquier argumento y lanzarlo si se llama más de una vez.

    – Pablo Manta

    28 de abril de 2017 a las 7:54

  • Esto es un Muy mal implementación singleton. En primer lugar, no es un decorador adecuado porque no utiliza functools.wraps o functools.update_wrapper. En segundo lugar, tener que obtener la instancia llamando Foo.Instance() es terriblemente poco pitónico y hay exactamente 0 razones por las que no podría haberse implementado como Foo() en lugar de. En tercer lugar, reemplazar la clase de esa manera produce resultados inesperados como type(Foo.instance()) is Foo -> False

    – Aran Fey

    28 de mayo de 2018 a las 12:59

  • @ Aran-Fey parece que esta solución realmente revienta tu burbuja jajaja. No creo que Paul Manta haya dicho que esta es la mejor solución en todo el mundo. Solo estaba tratando de responder la pregunta del autor original. Creo que es una gran solución para la “falta de ella” en python.

    – JokerMartini

    5 de julio de 2019 a las 15:17

1646746518 863 ¿Existe una manera simple y elegante de definir los singletons
jojo

Puede anular el __new__ método como este:

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


if __name__ == '__main__':
    s1 = Singleton()
    s2 = Singleton()
    if (id(s1) == id(s2)):
        print "Same"
    else:
        print "Different"

  • ADVERTENCIA: si __new__() devuelve una instancia de cls, entonces se invocará el método __init__() de la nueva instancia como __init__(self[, …]), donde self es la nueva instancia y los argumentos restantes son los mismos que se pasaron a __new__(). Si alguna subclase de Singleton implementa __init__(), se llamará varias veces con el mismo yo. Terminé usando una fábrica en su lugar.

    – alsuren

    8 de septiembre de 2011 a las 12:48


  • esto sería mejor usando una metaclase como respuesta aquí: stackoverflow.com/a/33201/804147

    – insuficiencia

    23 de enero de 2012 a las 15:24

  • Esto da la siguiente advertencia: singleton.py:9: DeprecationWarning: object.__new__() takes no parameters cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)

    – Siddhant

    2 de enero de 2013 a las 16:17


  • @Siddhant: peor aún, en Python 3, esa advertencia se convierte en un error. Ver bugs.python.org/issue1683368 y blog.jaraco.com/2014/05/… para más detalles.

    – Jason R. Coombs

    14 de junio de 2014 a las 16:36

1646746519 8 ¿Existe una manera simple y elegante de definir los singletons
Pedro Hoffman

Un enfoque ligeramente diferente para implementar el singleton en Python es el patrón de borgoña por Alex Martelli (empleado de Google y genio de Python).

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state

Entonces, en lugar de obligar a todas las instancias a tener la misma identidad, comparten estado.

1646746520 743 ¿Existe una manera simple y elegante de definir los singletons
Puntiagudo

El enfoque del módulo funciona bien. Si necesito absolutamente un singleton, prefiero el enfoque Metaclass.

class Singleton(type):
    def __init__(cls, name, bases, dict):
        super(Singleton, cls).__init__(name, bases, dict)
        cls.instance = None 

    def __call__(cls,*args,**kw):
        if cls.instance is None:
            cls.instance = super(Singleton, cls).__call__(*args, **kw)
        return cls.instance

class MyClass(object):
    __metaclass__ = Singleton

  • Este patrón va en contra del “Principio de responsabilidad única” (c2.com/cgi/wiki?Principio de responsabilidad única). Véase el punto (2) en blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx.

    – haridsv

    20 mayo 2010 a las 19:21

  • @haridsv No estoy de acuerdo. El hecho de que la clase sea un singleton es abstraído en la implementación de la metaclase: la clase en sí no sabe ni le importa que sea un singleton, ya que no está a cargo de hacer cumplir ese requisito, la metaclase sí lo está. Sin embargo, el método a continuación es claramente una violación, como usted nota. El método de la clase base está en algún punto intermedio.

    – agf

    23 de julio de 2011 a las 4:38

  • @dare2be: ¿No podría resolverse el problema de copia que menciona simplemente haciendo que la metaclase también agregue un __deepcopy__() método a la clase creada?

    – martineau

    3 de diciembre de 2012 a las 18:18

  • @martineau: Eso es type.__init__ es primordial, no MyClass.__init__

    –Eric

    03/02/2014 a las 0:00

  • Otro comentario de stackoverflow mencionó que puede corregir este error anulando new__() “` clase SingletonMeta(tipo): def _nuevo__(cls, nombre, bases, dict): dict[‘_deepcopy‘] = dictado[‘copy‘] = lambda self, *args: self return super(SingletonMeta, cls).__new__(cls, nombre, bases, dict) “` – stackoverflow.com/a/9887928/748503

    –James McGuigan

    24 de noviembre de 2017 a las 18:38

1646746520 407 ¿Existe una manera simple y elegante de definir los singletons
Pedro Mortensen

Ver esta implementación de PEP318implementando el patrón singleton con un decorador:

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...

  • Este patrón va en contra del “Principio de responsabilidad única” (c2.com/cgi/wiki?Principio de responsabilidad única). Véase el punto (2) en blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx.

    – haridsv

    20 mayo 2010 a las 19:21

  • @haridsv No estoy de acuerdo. El hecho de que la clase sea un singleton es abstraído en la implementación de la metaclase: la clase en sí no sabe ni le importa que sea un singleton, ya que no está a cargo de hacer cumplir ese requisito, la metaclase sí lo está. Sin embargo, el método a continuación es claramente una violación, como usted nota. El método de la clase base está en algún punto intermedio.

    – agf

    23 de julio de 2011 a las 4:38

  • @dare2be: ¿No podría resolverse el problema de copia que menciona simplemente haciendo que la metaclase también agregue un __deepcopy__() método a la clase creada?

    – martineau

    3 de diciembre de 2012 a las 18:18

  • @martineau: Eso es type.__init__ es primordial, no MyClass.__init__

    –Eric

    03/02/2014 a las 0:00

  • Otro comentario de stackoverflow mencionó que puede corregir este error anulando new__() “` clase SingletonMeta(tipo): def _nuevo__(cls, nombre, bases, dict): dict[‘_deepcopy‘] = dictado[‘copy‘] = lambda self, *args: self return super(SingletonMeta, cls).__new__(cls, nombre, bases, dict) “` – stackoverflow.com/a/9887928/748503

    –James McGuigan

    24 de noviembre de 2017 a las 18:38

los Documentación de Python cubre esto:

class Singleton(object):
    def __new__(cls, *args, **kwds):
        it = cls.__dict__.get("__it__")
        if it is not None:
            return it
        cls.__it__ = it = object.__new__(cls)
        it.init(*args, **kwds)
        return it
    def init(self, *args, **kwds):
        pass

Probablemente lo reescribiría para que se pareciera más a esto:

class Singleton(object):
    """Use to create a singleton"""
    def __new__(cls, *args, **kwds):
        """
        >>> s = Singleton()
        >>> p = Singleton()
        >>> id(s) == id(p)
        True
        """
        it_id = "__it__"
        # getattr will dip into base classes, so __dict__ must be used
        it = cls.__dict__.get(it_id, None)
        if it is not None:
            return it
        it = object.__new__(cls)
        setattr(cls, it_id, it)
        it.init(*args, **kwds)
        return it

    def init(self, *args, **kwds):
        pass


class A(Singleton):
    pass


class B(Singleton):
    pass


class C(A):
    pass


assert A() is A()
assert B() is B()
assert C() is C()
assert A() is not B()
assert C() is not B()
assert C() is not A()

Debería estar relativamente limpio para extender esto:

class Bus(Singleton):
    def init(self, label=None, *args, **kwds):
        self.label = label
        self.channels = [Channel("system"), Channel("app")]
        ...

  • +1 por ser el único que menciona la implementación de Guido van Rossum. Sin embargo, su propia versión es incorrecta: no debe usar hasattr y getattr en el interior __new__ como ambos llaman object.__getattribute__ que a su vez busca su "__self__" atributo a través de toda la jerarquía de clases en lugar de solo la clase actual. Si Guido usa __dict__ para el acceso de atributo que es por una razón. Tratar: class A(GuidoSingleton): pass, class B(A): pass, class C(YourSingleton): pass, class D(C): pass, print(A(), B(), C(), D()). Todas las subclases se refieren a la misma instancia con YourSingleton!

    – Maggyero

    15 de diciembre de 2017 a las 16:56

  • +1 por recordarnos que la documentación de Python es siempre el mejor lugar para comenzar nuestra búsqueda de singleton y otros patrones de diseño.

    – usuario-asterix

    13 de noviembre de 2018 a las 0:58


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad