patron examples diseƱo python singleton decorator base-class metaclass

examples - Creando un singleton en Python



python singleton examples (19)

Usa una metaclase

Recomendaría el Método # 2 , pero es mejor usar una metaclase que una clase base. Aquí hay una implementación de ejemplo:

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] class Logger(object): __metaclass__ = Singleton

O en Python3

class Logger(metaclass=Singleton): pass

Si desea ejecutar __init__ cada vez que se llama a la clase, agregue

else: cls._instances[cls].__init__(*args, **kwargs)

a la sentencia if en Singleton.__call__ .

Unas palabras sobre las metaclases. Una metaclase es la clase de una clase ; es decir, una clase es una instancia de su metaclase . Encontrará la metaclase de un objeto en Python con type(obj) . Las clases normales de nuevo estilo son de tipo type . Logger en el código anterior será de la class ''your_module.Singleton'' de tipo class ''your_module.Singleton'' , así como la (única) instancia de Logger será de la class ''your_module.Logger'' de tipo class ''your_module.Logger'' . Cuando llama al registrador con el Logger() , Python primero le pregunta a la metaclase del Logger , Singleton , qué hacer, permitiendo que la creación de instancias se anule. Este proceso es el mismo que Python que le pregunta a una clase qué hacer al llamar a __getattr__ cuando hace referencia a uno de sus atributos al hacer myclass.attribute .

Una metaclase esencialmente decide lo que significa la definición de una clase y cómo implementar esa definición. Véase, por ejemplo, http://code.activestate.com/recipes/498149/ , que esencialmente recrea las struct estilo C en Python utilizando metaclases. El hilo ¿Cuáles son sus casos de uso (concretos) para metaclases en Python? También proporciona algunos ejemplos, generalmente parecen estar relacionados con la programación declarativa, especialmente cuando se usan en ORMs.

En esta situación, si usa su Método # 2 , y una subclase define un método __new__ , se ejecutará cada vez que llame a SubClassOfSingleton() - porque es responsable de llamar al método que devuelve la instancia almacenada. Con una metaclase, solo se llamará una vez , cuando se cree la única instancia. Desea personalizar lo que significa llamar a la clase , que se decide por su tipo.

En general, tiene sentido usar una metaclase para implementar un singleton. Un singleton es especial porque se crea solo una vez , y una metaclase es la forma en que se personaliza la creación de una clase . El uso de una metaclase le da más control en caso de que necesite personalizar las definiciones de clase singleton de otras maneras.

Sus singletons no necesitarán herencia múltiple (porque la metaclase no es una clase base), pero para las subclases de la clase creada que usan herencia múltiple, debe asegurarse de que la clase singleton sea la primera / izquierda con una metaclase que redefine __call__ Es muy poco probable que esto sea un problema. El dictado de la instancia no está en el espacio de nombres de la instancia, por lo que no se sobrescribirá accidentalmente.

También escuchará que el patrón de singleton viola el "Principio de Responsabilidad Única" - cada clase debe hacer solo una cosa . De esa manera, no tiene que preocuparse por arruinar una cosa que hace el código si necesita cambiar otra, porque están separadas y encapsuladas. La implementación de metaclase pasa esta prueba . La metaclase es responsable de imponer el patrón y la clase y las subclases creadas no deben ser conscientes de que son singletons . El método # 1 no pasa esta prueba, como anotó con "MyClass en sí misma es una función, no una clase, por lo que no puede llamar métodos de clase desde ella".

Versión compatible con Python 2 y 3

Escribir algo que funcione tanto en Python2 como en 3 requiere un esquema un poco más complicado. Dado que las metaclases suelen ser subclases de tipo de type , es posible usar una para crear dinámicamente una clase base intermediaria en tiempo de ejecución con ella como su metaclase y luego usarla como la clase base de la clase base pública Singleton . Es más difícil de explicar que de hacer, como se ilustra a continuación:

# works in Python 2 & 3 class _Singleton(type): """ A metaclass that creates a Singleton base class when called. """ _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] class Singleton(_Singleton(''SingletonMeta'', (object,), {})): pass class Logger(Singleton): pass

Un aspecto irónico de este enfoque es que utiliza subclases para implementar una metaclase. Una posible ventaja es que, a diferencia de una metaclase pura, isinstance(inst, Singleton) devolverá True .

Correcciones

En otro tema, probablemente ya haya notado esto, pero la implementación de la clase base en su publicación original es incorrecta. _instances deben ser referenciadas en la clase , debes usar super() o estás recurriendo , y __new__ es en realidad un método estático al que debes pasar la clase , no un método de clase, ya que la clase real no tiene Se ha creado aún cuando se llama. Todas estas cosas también serán ciertas para una implementación de metaclase.

class Singleton(object): _instances = {} def __new__(class_, *args, **kwargs): if class_ not in class_._instances: class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs) return class_._instances[class_] class MyClass(Singleton): pass c = MyClass()

Decorador regresando una clase

Originalmente estaba escribiendo un comentario pero era demasiado largo, así que lo agregaré aquí. El método # 4 es mejor que la otra versión de decorador, pero es más código del necesario para un singleton, y no es tan claro lo que hace.

Los principales problemas provienen de la clase es su propia clase base. Primero, ¿no es extraño que una clase sea una subclase de una clase casi idéntica con el mismo nombre que existe solo en su atributo __class__ ? Esto también significa que no puede definir ningún método que llame al método del mismo nombre en su clase base con super() porque recursionarán. Esto significa que su clase no puede personalizar __new__ , y no puede derivar de ninguna clase que necesite __init__ llamada.

Cuando usar el patrón singleton

Su caso de uso es uno de los mejores ejemplos de querer usar un singleton. Dice en uno de los comentarios "Para mí, el registro siempre ha sido un candidato natural para Singletons". Tienes toda la razón .

Cuando la gente dice que los singletons son malos, la razón más común es que son estados compartidos implícitos . Mientras que las variables globales y las importaciones de módulos de nivel superior son estados compartidos explícitos , generalmente se crea una instancia de otros objetos que se transmiten. Este es un buen punto, con dos excepciones .

El primero, y uno que se menciona en varios lugares, es cuando los singletons son constantes . El uso de constantes globales, especialmente las enumeraciones, se acepta ampliamente y se considera sensato porque, sin importar qué, ninguno de los usuarios puede desordenarlos con otros usuarios . Esto es igualmente cierto para un singleton constante.

La segunda excepción, que se menciona menos, es la opuesta: cuando el singleton es solo un receptor de datos , no una fuente de datos (directa o indirectamente). Esta es la razón por la que los madereros se sienten como un uso "natural" para singletons. Como los diferentes usuarios no están cambiando los registradores de manera que otros usuarios se preocupen, realmente no hay estado compartido . Esto niega el argumento principal en contra del patrón de singleton, y los convierte en una opción razonable debido a su facilidad de uso para la tarea.

Aquí hay una cita de http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html :

Ahora, hay un tipo de Singleton que está bien. Ese es un singleton donde todos los objetos alcanzables son inmutables. Si todos los objetos son inmutables, Singleton no tiene un estado global, ya que todo es constante. Pero es tan fácil convertir este tipo de singleton en uno mutable, es una pendiente muy resbaladiza. Por lo tanto, yo también estoy en contra de estos Singletons, no porque sean malos, sino porque es muy fácil que se vuelvan malos. (Como nota al margen, la enumeración de Java son solo este tipo de singletons. Siempre que no ponga un estado en su enumeración, está bien, así que no lo haga).

El otro tipo de Singletons, que son semi-aceptables, son aquellos que no afectan la ejecución de su código, no tienen "efectos secundarios". El registro es un ejemplo perfecto. Se carga con Singletons y estado global. Es aceptable (ya que no le hará daño) porque su aplicación no se comporta de manera diferente, ya sea que un registrador dado esté habilitado o no. La información aquí fluye de una manera: desde su aplicación al registrador. Incluso los registradores de pensamiento son estados globales, ya que no hay información que fluya de los registradores a su aplicación, los registradores son aceptables Aún debe inyectar su registrador si desea que su prueba afirme que algo se está registrando, pero en general los registradores no son perjudiciales a pesar de estar llenos de estado.

Esta pregunta no es para la discusión de si el patrón de diseño de singleton es deseable o no, es un antipatrón o para cualquier guerra religiosa, sino para discutir cómo este patrón se implementa mejor en Python de tal manera que sea la mayoría de los pitónicos. En este caso, defino que "la mayoría de los pitones" significa que sigue el "principio de menos 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 desordenar varias clases con gumph agregado 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

Pros

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

Contras

  • Mientras que los objetos creados usando MyClass () serían verdaderos objetos singleton, MyClass en sí misma es una función, no una clase, por lo que no se pueden llamar métodos de clase. También para m = MyClass(); n = MyClass(); o = type(n)(); m = MyClass(); n = MyClass(); o = type(n)(); entonces m == n && m != o && n != o

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

Pros

  • Es una verdadera clase

Contras

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

Método 3: una metaclass

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

Pros

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

Contras

  • Hay alguna

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

Pros

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

Contras

  • ¿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, hay una cuestión de debate sobre si sería demasiado fácil escalar este patrón ...
  • ¿Cuál es el punto del atributo _sealed
  • No se pueden llamar a los métodos del mismo nombre en las clases base mediante super() porque se repetirán. Esto significa que no puede personalizar __new__ y no puede subclasificar una clase que necesite que llame a __init__ .

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 método de 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. 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. Limitations: The decorated class cannot be inherited from. """ 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)


Aquí hay una frase para usted:

singleton = lambda c: c()

Así es como lo usas:

@singleton class wat(object): def __init__(self): self.x = 1 def get_x(self): return self.x assert wat.get_x() == 1

Su objeto se instancia con entusiasmo. Esto puede o no ser lo que quieres.


Bueno, aparte de estar de acuerdo con la sugerencia general de Pythonic sobre tener un nivel de módulo global, ¿qué tal esto?

def singleton(class_): class class_w(class_): _instance = None def __new__(class2, *args, **kwargs): if class_w._instance is None: class_w._instance = super(class_w, class2).__new__(class2, *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(object): def __init__(self, text): print text @classmethod def name(class_): print class_.__name__ x = MyClass(111) x.name() y = MyClass(222) print id(x) == id(y)

La salida es:

111 # the __init__ is called only on the 1st time MyClass # the __name__ is preserved True # this is actually the same instance



El método 3 parece ser muy bueno, pero si quieres que tu programa se ejecute tanto en Python 2 como en Python 3 , no funciona. Incluso la protección de las variantes separadas con pruebas para la versión de Python falla, porque la versión de Python 3 da un error de sintaxis en Python 2.

Gracias a Mike Watkins: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ . Si desea que el programa funcione tanto en Python 2 como en Python 3, necesita hacer algo como:

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] MC = Singleton(''MC'', (object), {}) class MyClass(MC): pass # Code for the class implementation

Supongo que el ''objeto'' en la asignación debe reemplazarse con el ''BaseClass'', pero no lo he intentado (he intentado el código como se ilustra).


Las respuestas anteriores son correctas, pero no estoy de acuerdo con la declaración que se publicó como parte de la pregunta en el Método 1 "MyClass en sí misma es una función, no una clase, por lo que no puede llamar a los métodos de clase". Vea mi ejemplo a continuación que el método se llama varias veces, en MyClass, que está decorado con una etiqueta singleton.

Además, tenga en cuenta que esto es muy similar a algunas de las respuestas publicadas y se basa en el documento de Python, pero es ligeramente diferente porque la clase y la función están diseñadas de manera que pueden recibir 1 o 0 argumentos y aún así funciona singleton.

Aquí está la prueba de que puede llamar a los métodos de singleton varias veces y muestra que una instancia de la clase todavía se usa y no se crean nuevos objetos.

#/usr/bin/env python def singleton(cls): instances = {} def getinstance(anyArgs=None): if cls not in instances: instances[cls] = cls(anyArgs) return instances[cls] return getinstance @singleton class MyClass: def __init__(self,count=None): print("print argument if any exists",count) def test(self, counter): # time.sleep(1000) print("-->",counter) return counter ### create two objects to see if we get a singleton behavior ! a = MyClass(10000) a.test(1) b = MyClass() b.test(2) if a != b: print("this is not a singleton") #lets makesure it''s still is the same object if a!=b: print("error")

Dado que theheadofabroom (la persona que publicó la pregunta) había dado algunos comentarios sobre su pregunta original, fui y trabajé en una nueva solución basada en sus comentarios (aún conservé mi respuesta anterior porque creo que puede ser útil para algunos, aunque no 100% exactamente lo que el jefe de cabeza está preguntando). Aquí está la respuesta actualizada:

Aquí está el código para copiarlo y pegarlo :)

#/usr/bin/env python from functools import wraps def singleton(cls): instances = {} def getinstance(anyArgs=None): if cls not in instances: instances[cls] = cls(anyArgs) return instances[cls] return getinstance def add_function(cls): def outer_decorate_it(somefunction): @wraps(somefunction) def wrapper( *args, **kwargs): return somefunction(*args, **kwargs) setattr(cls, somefunction.__name__, wrapper) return somefunction return outer_decorate_it @singleton class MyClass(): def __init__(self,count=None): print("print argument if any exists",count) @add_function(MyClass) def testit(): print("It''s me the function from the class") MyClass.testit()


Probablemente nunca necesites un singleton en Python. Simplemente defina todos sus datos y funciones en un módulo y tendrá un singleton de facto.

Si realmente tienes que tener una clase de singleton, me gustaría ir con:

class My_Singleton(object): def foo(self): pass my_singleton = My_Singleton()

Usar:

from mysingleton import my_singleton my_singleton.foo()

donde mysingleton.py es su nombre de archivo en el que se define My_Singleton. Esto funciona porque después de la primera importación de un archivo, Python no vuelve a ejecutar el código.


Qué tal esto:

def singleton(cls): instance=cls() cls.__new__ = cls.__call__= lambda cls: instance cls.__init__ = lambda self: None return instance

Úsalo como decorador en una clase que debería ser un singleton. Me gusta esto:

@singleton class MySingleton: #....

Esto es similar al decorador singleton = lambda c: c() en otra respuesta. Al igual que la otra solución, la única instancia tiene el nombre de la clase ( MySingleton ). Sin embargo, con esta solución aún puede "crear" instancias (en realidad obtener la única instancia) de la clase, haciendo MySingleton() . También le impide crear instancias adicionales al hacer type(MySingleton)() (que también devuelve la misma instancia).


Usa un módulo. Se importa solo una vez. Defina algunas variables globales en él, serán los ''atributos'' de singleton. Agregue algunas funciones - los ''métodos'' del singleton.


Es ligeramente similar a la respuesta de fab pero no es exactamente la misma.

El contrato singleton no requiere que podamos llamar al constructor varias veces. Como un singleton debe crearse una vez y solo una vez, ¿no debería verse como creado solo una vez? "Spoofing", el constructor podría decirse que perjudica la legibilidad.

Así que mi sugerencia es sólo esto:

class Elvis(): def __init__(self): if hasattr(self.__class__, ''instance''): raise Exception() self.__class__.instance = self # initialisation code... @staticmethod def the(): if hasattr(Elvis, ''instance''): return Elvis.instance return Elvis()

Esto no descarta el uso del constructor o el campo instancepor código de usuario:

if Elvis() is King.instance:

... si está seguro de que Elvisaún no se ha creado y de que se Kingha creado .

Pero alienta a los usuarios a utilizar el themétodo universalmente:

Elvis.the().leave(Building.the())

Para completar esto, también puede anular la __delattr__()creación de una Excepción si se intenta eliminar instancey anularla __del__()para que genere una Excepción (a menos que sepamos que el programa está finalizando ...)

Futuras mejoras

Mi agradecimiento a aquellos que han ayudado con comentarios y ediciones, de los cuales más son bienvenidos. Mientras uso Jython, esto debería funcionar de manera más general y ser seguro para subprocesos.

try: # This is jython-specific from synchronize import make_synchronized except ImportError: # This should work across different python implementations def make_synchronized(func): import threading func.__lock__ = threading.Lock() def synced_func(*args, **kws): with func.__lock__: return func(*args, **kws) return synced_func class Elvis(object): # NB must be subclass of object to use __new__ instance = None @classmethod @make_synchronized def __new__(cls, *args, **kwargs): if cls.instance is not None: raise Exception() cls.instance = object.__new__(cls, *args, **kwargs) return cls.instance def __init__(self): pass # initialisation code... @classmethod @make_synchronized def the(cls): if cls.instance is not None: return cls.instance return cls()

Puntos de nota:

  1. Si no hace una subclase del objeto en python2.x obtendrá una clase de estilo antiguo, que no usa __new__
  2. Al decorar __new__, debe decorar con @classmethod o __new__será un método de instancia independiente.
  3. Esto posiblemente podría mejorarse mediante el uso de una metaclase, ya que esto le permitiría hacer theuna propiedad de nivel de clase, posiblemente cambiándole el nombre ainstance

Esta es mi forma preferida de implementar singletons:

class Test(object): obj = None def __init__(self): if Test.obj is not None: raise Exception(''A Test Singleton instance already exists'') # Initialization code here @classmethod def get_instance(cls): if cls.obj is None: cls.obj = Test() return cls.obj @classmethod def custom_method(cls): obj = cls.get_instance() # Custom Code here


Esta solución causa cierta contaminación en el espacio de nombres a nivel de módulo (tres definiciones en lugar de solo una), pero me resulta fácil de seguir.

Me gustaría poder escribir algo como esto (inicialización perezosa), pero desafortunadamente las clases no están disponibles en el cuerpo de sus propias definiciones.

# wouldn''t it be nice if we could do this? class Foo(object): instance = None def __new__(cls): if cls.instance is None: cls.instance = object() cls.instance.__class__ = Foo return cls.instance

Como eso no es posible, podemos romper la inicialización y la instancia estática en

Inicialización impaciente:

import random class FooMaker(object): def __init__(self, *args): self._count = random.random() self._args = args class Foo(object): def __new__(self): return foo_instance foo_instance = FooMaker() foo_instance.__class__ = Foo

Inicialización perezosa:

Inicialización impaciente:

import random class FooMaker(object): def __init__(self, *args): self._count = random.random() self._args = args class Foo(object): def __new__(self): global foo_instance if foo_instance is None: foo_instance = FooMaker() return foo_instance foo_instance = None


No recuerdo dónde encontré esta solución, pero me parece que es la más "elegante" desde mi punto de vista no experto en Python:

class SomeSingleton(dict): __instance__ = None def __new__(cls, *args,**kwargs): if SomeSingleton.__instance__ is None: SomeSingleton.__instance__ = dict.__new__(cls) return SomeSingleton.__instance__ def __init__(self): pass def some_func(self,arg): pass

¿Por qué me gusta esto? Sin decoradores, sin clases meta, sin herencia múltiple ... y si decide que ya no quiere que sea un Singleton, simplemente elimine el __new__método. Como soy nuevo en Python (y OOP en general) espero que alguien me explique por qué este es un enfoque terrible.


Una línea (no estoy orgulloso, pero hace el trabajo):

class Myclass: def __init__(self): # do your stuff globals()[type(self).__name__] = lambda: self # singletonify


Código basado en la respuesta de Tolli .

#decorator, modyfies new_cls def _singleton(new_cls): instance = new_cls() #2 def new(cls): if isinstance(instance, cls): #4 return instance else: raise TypeError("I can only return instance of {}, caller wanted {}".format(new_cls, cls)) new_cls.__new__ = new #3 new_cls.__init__ = lambda self: None #5 return new_cls #decorator, creates new class def singleton(cls): new_cls = type(''singleton({})''.format(cls.__name__), (cls,), {} ) #1 return _singleton(new_cls) #metaclass def meta_singleton(name, bases, attrs): new_cls = type(name, bases, attrs) #1 return _singleton(new_cls)

Explicación:

  1. Crear una nueva clase, heredando de dado cls
    (no se modifica clsen caso de que alguien quiera, por ejemplo singleton(list))

  2. Crear instancia. Antes de anular __new__es tan fácil.

  3. Ahora, cuando hemos creado una instancia fácilmente, reemplaza el __new__uso de un método definido hace un momento.
  4. La función devuelve instancesolo cuando es lo que la persona que llama espera, de lo contrario aumenta TypeError.
    La condición no se cumple cuando alguien intenta heredar de una clase decorada.

  5. Si __new__()devuelve una instancia de cls, el __init__()método de la nueva instancia se invocará como __init__(self[, ...]), donde self es la nueva instancia y los argumentos restantes son los mismos que se pasaron __new__().

    instanceya está inicializado, por lo que la función reemplaza __init__a la función que no hace nada.

Véalo trabajando en línea


Esta respuesta probablemente no sea lo que estás buscando. Quería un singleton en el sentido de que solo ese objeto tenía su identidad, para comparación. En mi caso, estaba siendo utilizado como un valor de Sentinel . Para lo cual la respuesta es muy simple, haga cualquier objeto mything = object()y por la naturaleza de Python, solo esa cosa tendrá su identidad.

#!python MyNone = object() # The singleton for item in my_list: if item is MyNone: # An Example identity comparison raise StopIteration


Voy a tirar la mía en el ring. Es un decorador sencillo.

from abc import ABC def singleton(real_cls): class SingletonFactory(ABC): instance = None def __new__(cls, *args, **kwargs): if not cls.instance: cls.instance = real_cls(*args, **kwargs) return cls.instance SingletonFactory.register(real_cls) return SingletonFactory # Usage @singleton class YourClass: ... # Your normal implementation, no special requirements.

Beneficios creo que tiene sobre algunas de las otras soluciones:

  • Es claro y conciso (a mi ojo; D).
  • Su acción está completamente encapsulada. No necesitas cambiar nada de la implementación de YourClass. Esto incluye no tener que usar una metaclase para su clase (tenga en cuenta que la metaclase anterior está en la fábrica, no en la clase "real").
  • No se basa en parches de mono nada.
  • Es transparente para las personas que llaman:
    • Las personas que llaman todavía importan YourClass, se parece a una clase (porque lo es), y la usan normalmente. No hay necesidad de adaptar a las personas que llaman a una función de fábrica.
    • Lo que YourClass()crea una instancia sigue siendo una verdadera instancia de lo YourClassque implementó, no un proxy de ningún tipo, por lo que no existe la posibilidad de que se produzcan efectos secundarios.
    • isinstance(instance, YourClass) y las operaciones similares siguen funcionando como se esperaba (aunque este bit requiere abc, por lo que excluye a Python <2.6).

Se me ocurre una desventaja: los métodos de clase y los métodos estáticos de la clase real no se pueden llamar de forma transparente a través de la clase de fábrica que lo oculta. Lo he usado raramente para que nunca me haya topado con esa necesidad, pero se rectificaría fácilmente mediante el uso de una metaclase personalizada en la fábrica que implemente __getattr__()para delegar el acceso de atributos todo a la clase real.

Un patrón relacionado que en realidad he encontrado más útil (no es que diga que este tipo de cosas se requieren muy a menudo) es un patrón "Único" en el que crear una instancia de la clase con los mismos argumentos hace que se recupere la misma instancia. Es decir, un "singleton por argumentos". Lo anterior se adapta a este pozo y se vuelve aún más conciso:

def unique(real_cls): class UniqueFactory(ABC): @functools.lru_cache(None) # Handy for 3.2+, but use any memoization decorator you like def __new__(cls, *args, **kwargs): return real_cls(*args, **kwargs) UniqueFactory.register(real_cls) return UniqueFactory

Dicho todo esto, estoy de acuerdo con el consejo general de que si crees que necesitas una de estas cosas, deberías detenerte por un momento y preguntarte si realmente lo haces. El 99% del tiempo, YAGNI.


class Foo(object): pass some_global_variable = Foo()

Los módulos se importan solo una vez, todo lo demás se está pensando demasiado. No uses singletons y trata de no usar globales.