pattern patron examples diseño python design-patterns singleton

python - patron - singleton pattern



¿Hay una manera simple y elegante de definir singletons? (22)

Parece que hay muchas formas de definir singletons en Python. ¿Hay una opinión consensuada sobre Stack Overflow?


class Singeltone(type): instances = dict() def __call__(cls, *args, **kwargs): if cls.__name__ not in Singeltone.instances: Singeltone.instances[cls.__name__] = type.__call__(cls, *args, **kwargs) return Singeltone.instances[cls.__name__] class Test(object): __metaclass__ = Singeltone inst0 = Test() inst1 = Test() print(id(inst1) == id(inst0))


Aquí hay un ejemplo del Python IAQ de Peter Norvig. ¿Cómo hago el patrón Singleton en Python? (Debe utilizar la función de búsqueda de su navegador para encontrar esta pregunta, no hay un enlace directo, lo siento)

También Bruce Eckel tiene otro ejemplo en su libro Thinking in Python (de nuevo no hay un enlace directo al código)



La única vez que escribí un singleton en Python usé una clase donde todas las funciones miembro tenían el decorador classmethod.

class foo: x = 1 @classmethod def increment(cls, y = 1): cls.x += y


Estoy muy inseguro sobre esto, pero mi proyecto usa ''singletons de convención'' (no forzado singletons9, es decir, si tengo una clase llamada DataController, defino esto en el mismo módulo:

_data_controller = None def GetDataController(): global _data_controller if _data_controller is None: _data_controller = DataController() return _data_controller

No es elegante, ya que es un total de seis líneas. Pero todos mis singletons usan este patrón, y al menos es muy explícito (que es pitónico).


class Singleton(object[,...]): staticVar1 = None staticVar2 = None def __init__(self): if self.__class__.staticVar1==None : # create class instance variable for instantiation of class # assign class instance variable values to class static variables else: # assign class static variable values to class instance variables


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



Mi solución simple que se basa en el valor predeterminado de los parámetros de la función.

def getSystemContext(contextObjList=[]): if len( contextObjList ) == 0: contextObjList.append( Context() ) pass return contextObjList[0] class Context(object): # Anything you want here


Crear un decorador singleton (también conocido como una anotación) es una forma elegante si desea decorar (anotar) las clases en el futuro. Luego, simplemente coloca @singleton antes de la definición de su clase.

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


Puede anular el método __new__ esta manera:

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"


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 podría ser instanciado repetidamente.

Si desea utilizar una clase, no hay forma de crear clases privadas o constructores privados en Python, por lo que no puede protegerse contra múltiples instancias, a excepción 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 singleton.


Siendo relativamente nuevo en Python, no estoy seguro de cuál es el modismo más común, pero lo más simple que puedo pensar es simplemente usar un módulo en lugar de una clase. Lo que habrían sido los métodos de instancia en su clase se convertirán en funciones en el módulo y cualquier dato simplemente se convierte en variables en el módulo en lugar de miembros de la clase. Sospecho que este es el enfoque pitónico para resolver el tipo de problema para el que las personas usan singletons.

Si realmente quieres una clase singleton, hay una implementación razonable descrita en el primer hit en Google para "singleton de Python", específicamente:

class Singleton: __single = None def __init__( self ): if Singleton.__single: raise Singleton.__single Singleton.__single = self

Eso parece hacer el truco.


Un enfoque ligeramente diferente para implementar el singleton en Python es el patrón borg de 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 forzar a todas las instancias a tener la misma identidad, comparten el estado.


Creo que obligar a una clase o instancia a ser singleton es excesivo. Personalmente, me gusta definir una clase de instanciación normal, una referencia semiprivada y una función simple de fábrica.

class NothingSpecial: pass _the_one_and_only = None def TheOneAndOnly(): global _the_one_and_only if not _the_one_and_only: _the_one_and_only = NothingSpecial() return _the_one_and_only

O si no hay problemas para crear instancias cuando el módulo se importa por primera vez:

class NothingSpecial: pass THE_ONE_AND_ONLY = NothingSpecial()

De esta forma, puede escribir pruebas en instancias nuevas sin efectos secundarios, y no es necesario rociar el módulo con declaraciones globales, y si es necesario, puede derivar variantes en el futuro.


De acuerdo, Singleton podría ser bueno o malo, lo sé. Esta es mi implementación, y simplemente extiendo un enfoque clásico para introducir un caché dentro y producir muchas instancias de un tipo diferente o, muchas instancias del mismo tipo, pero con diferentes argumentos.

Lo llamé Singleton_group, porque agrupa instancias similares entre sí e impide que se cree un objeto de la misma clase, con los mismos argumentos:

# Peppelinux''s cached singleton class Singleton_group(object): __instances_args_dict = {} def __new__(cls, *args, **kwargs): if not cls.__instances_args_dict.get((cls.__name__, args, str(kwargs))): cls.__instances_args_dict[(cls.__name__, args, str(kwargs))] = super(Singleton_group, cls).__new__(cls, *args, **kwargs) return cls.__instances_args_dict.get((cls.__name__, args, str(kwargs))) # It''s a dummy real world use example: class test(Singleton_group): def __init__(self, salute): self.salute = salute a = test(''bye'') b = test(''hi'') c = test(''bye'') d = test(''hi'') e = test(''goodbye'') f = test(''goodbye'') id(a) 3070148780L id(b) 3070148908L id(c) 3070148780L b == d True b._Singleton_group__instances_args_dict {(''test'', (''bye'',), ''{}''): <__main__.test object at 0xb6fec0ac>, (''test'', (''goodbye'',), ''{}''): <__main__.test object at 0xb6fec32c>, (''test'', (''hi'',), ''{}''): <__main__.test object at 0xb6fec12c>}

Cada objeto lleva el caché de singleton ... Esto podría ser malo, pero funciona muy bien para algunos :)


En los casos en que no desea la solución basada en metaclass anterior, y no le gusta el enfoque simple basado en el decorador de funciones (por ejemplo, porque en ese caso los métodos estáticos en la clase singleton no funcionarán), este compromiso funciona:

class singleton(object): """Singleton decorator.""" def __init__(self, cls): self.__dict__[''cls''] = cls instances = {} def __call__(self): if self.cls not in self.instances: self.instances[self.cls] = self.cls() return self.instances[self.cls] def __getattr__(self, attr): return getattr(self.__dict__[''cls''], attr) def __setattr__(self, attr, value): return setattr(self.__dict__[''cls''], attr, value)


La 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 vuelva a escribir para que se parezca 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 """ self = "__self__" if not hasattr(cls, self): instance = object.__new__(cls) instance.init(*args, **kwds) setattr(cls, self, instance) return getattr(cls, self) def init(self, *args, **kwds): pass

Debería ser relativamente limpio extender esto:

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


Vea esta implementación desde PEP318 , implementando el patrón de 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: ...


Medio hermano de Singleton

Estoy totalmente de acuerdo con Staale y dejo aquí una muestra de cómo crear un medio hermano singleton:

class void:pass a = void(); a.__class__ = Singleton

a reportará ahora que es de la misma clase que singleton incluso si no se ve así. Así que los singletons que usan clases complicadas terminan dependiendo de que no nos equivoquemos mucho con ellos.

Siendo así, podemos tener el mismo efecto y usar cosas más simples como una variable o un módulo. Aún así, si queremos utilizar las clases para mayor claridad y porque en Python una clase es un objeto , entonces ya tenemos el objeto (no e instancia, pero sí lo hará).

class Singleton: def __new__(cls): raise AssertionError # Singletons can''t have instances

Ahí tenemos un buen error de aserción si tratamos de crear una instancia, y podemos almacenar derivaciones en miembros estáticos y hacer cambios en ellas en tiempo de ejecución (I love Python). Este objeto es tan bueno como otros sobre medios hermanos (aún puedes crearlos si lo deseas), sin embargo, tenderá a correr más rápido debido a la simplicidad.


Aquí está mi propia implementación de singletons. Todo lo que tienes que hacer es decorar la clase; para obtener el singleton, debes usar el método Instance . 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)


Como dice la respuesta aceptada , la forma más idiomática es simplemente usar un módulo .

Con eso en mente, aquí hay una prueba de concepto:

def singleton(cls): obj = cls() # Always return the same object cls.__new__ = staticmethod(lambda cls: obj) # Disable __init__ try: del cls.__init__ except AttributeError: pass return cls

Vea el modelo de datos de Python para más detalles sobre __new__ .

Ejemplo:

@singleton class Duck(object): pass if Duck() is Duck(): print "It works!" else: print "It doesn''t work!"

Notas:

  1. Tienes que usar clases de estilo nuevo (derivadas del object ) para esto.

  2. El singleton se inicializa cuando está definido, en lugar de la primera vez que se usa.

  3. Esto es solo un ejemplo de juguete. Nunca he usado esto en el código de producción, y no pienso hacerlo.