Creando un singleton en Python
Frecuentes
Visto 445,830 veces
1232
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
0 Respuestas
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas python singleton decorator base-class metaclass or haz tu propia pregunta.
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 insistesFoo.x
en lugar deFoo().x
); usar atributos de clase y métodos estáticos / de clase (Foo.x
). - Chris Morgan@ChrisMorgan: Si vas a usar métodos de clase / estáticos solamente, entonces no te molestes en hacer una clase, de verdad. - Cat Plus Plus
Duplicado de stackoverflow.com/questions/42558/… y stackoverflow.com/questions/31875/…. - agf
@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. - theheadofabroom
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. - Hejazzman