2012-03-02 7 views
5

Tengo una clase que tiene el formato singleton python estándar que uso. También tiene un único valor, pero en lugar de iniciarlo en cada instancia, lo inicio en __new__() cuando realizo el manejo de singleton. ¿Es esta la manera pitónica de hacerlo? Si no, ¿de qué otra manera puedo?¿Es un 'pecado original' hacer trabajo de inicio en __new__? Si es así, ¿cuál es la mejor forma de manejar run once init para singletons?

class Foo(object): 
    _inst = None 
    def __new__(cls, val): 
     if Foo._inst is None: 
      Foo._inst = super(Foo, self).__new__(cls) 
      Foo._inst.val = val 
     return Foo._inst 

Esta es, obviamente, la forma simplificada. He utilizado la construcción de esperar hasta __init__() para verificar y configurar Foo._inst para manejar ejecutar una vez init para un singleton, pero parece que está listo para errores de sincronización en una situación de multiproceso. Para situaciones donde no hay valores que puedan corromperse en la instancia existente si __init__() se ejecuta nuevamente, no hay nada de malo en poner el init en __init__(), excepto que está ejecutando código que efectivamente no hace nada cuando no es necesario, pero a menudo hay valores de estado corruptibles que no quiero sobrescribir. Sé que los singletons también son malos, pero a veces son la mejor solución (como definir tipos de uso especiales que deseo verificar con is).

Esto parece ser la mejor solución que se me ocurre, pero me siento sucio al inicializar valores de instancia en __new__(). ¿Alguien tiene ideas sobre cómo manejar esto mejor?

+7

Es un pecado original para usar singletons. Son una forma de agregar inconvenientes adicionales y restricciones extrañas a los globales. Si necesita estado global, use globales, pero mejor intente evitarlos. Nunca use singletons. –

+1

@Sven Marnach Hago lo que puedo para evitar singletons y globales, por supuesto, pero hay casos en que son la mejor solución. Por ejemplo, acabo de implementar una clase de Ternary, pero no necesito más de una instancia de cada tipo (True, False, Unknown), y en realidad, tener solo uno de cada uno me permite usarlo más como un bool al permitir el 'es' operador para trabajar. Por lo tanto, aunque se deben evitar los singleton, debería haber buenas maneras de usarlos cuando sean la mejor opción. –

+3

¿No te refieres a "pecado cardinal"? –

Respuesta

2

Para responder a la primera pregunta: Los Singletons no se usan con demasiada frecuencia en Python ya que en realidad no son necesarios.

responder a la segunda: Si cree que realmente los necesita, hay un poco más limpio patrón, similar llamado "Borg", que se utiliza a veces, ver: the Borg design pattern Si necesita compartir sólo algunos atributos entre instancias y Don 't necesidad de Singleton en toda regla (que se puede instanciar una vez) simplemente puede utilizar atributos de clase de este modo:

class Foo(object): 
    val = ... 

    def __init__(self): 
     ... 

a continuación, todas las instancias compartirán el mismo valor del atributo 'val'.

+0

Pude haber simplificado demasiado mi ejemplo ... en el ejemplo más común, en realidad tengo un diccionario de instancia almacenado en la clase, y mi objetivo es limitar el número de instancias de tipo especial. Entonces terminas con 'Foo._insts = {1: Foo (1), 2: Foo (2), 3: Foo (3)}'. Luego terminas con un tipo único para los 3 valores, pero solo una instancia por valor. –

+0

Python no es un lenguaje que permita algo tan estricto, es decir, probablemente no podrá protegerse al 100% de todos modos. Sin embargo, si solo quiere hacer una interfaz decente para eso, ¿por qué no hacer dos clases, una de las cuales es un registro y permite crear instancias de otras si no se ha excedido el límite? es decir, Foo() y FooRegistry() y tienen un método FooRegistry.create() que devuelve la instancia de Foo si no se ha excedido el límite o si se ha producido una excepción o si se trata de algo más para manejar la situación ...? – kgr

+0

¿Por qué agregar otra capa de clases para hacer lo mismo? Esto produce el colapso de FooRegistery y Foo en una única clase donde FooRegistery.create() se convierte en Foo .__ new __(). –

1

No hay mejor manera de crear singletons. Si lo estás buscando (y la lógica de tres valores es el único ejemplo en el que puedo pensar si esto tiene sentido), puedes hacerlo de la forma en que lo haces.

+0

Sí, ese era mi caso de uso para el tipo de datos [Python Tribool] (http://www.grantjenks.com/docs/tribool/). – GrantJ

4

Siempre puede usar variables globales en lugar de singletons. Ejemplo para una clase con tres instancias diferentes:

class _Ternary(object): 
    """Internal class. Don't instantiate!""" 
    def __init__(self, value): 
     self.value = value 

ternary_0 = _Ternary(0) 
ternary_1 = _Ternary(1) 
ternary_2 = _Ternary(2) 

def ternary(value): 
    """Factory function to request a "ternary" instance.""" 
    return {0: ternary_0, 1: ternary_1: 2: ternary_2}[value] 

Esto es directo y evitará el dolor de los singletons.

+0

Pero con este patrón, ¿cómo hace que la sobrecarga del operador funcione bien? Por ejemplo, ¿cómo harías 'ternary_1 - ternary_1' return' ternary_0'? – GrantJ

+0

@GrantJ: No entiendo el problema. Definir un método '__sub__' en la clase' Ternary' –

Cuestiones relacionadas