Parece que hay muchas maneras de definir solteros en Python. ¿Existe una opinión consensuada sobre Stack Overflow?
¿Existe una manera simple y elegante de definir los singletons? [duplicate]
Jaime
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éslogging
debes llamarlogging.shutdown()
. Esto significa que debe importarlogging
en el módulo que llamashutdown
. 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
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
ofunctools.update_wrapper
. En segundo lugar, tener que obtener la instancia llamandoFoo.Instance()
es terriblemente poco pitónico y hay exactamente 0 razones por las que no podría haberse implementado comoFoo()
en lugar de. En tercer lugar, reemplazar la clase de esa manera produce resultados inesperados comotype(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
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
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.
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, noMyClass.__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
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, noMyClass.__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
ygetattr
en el interior__new__
como ambos llamanobject.__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 conYourSingleton
!– 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
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