2011-06-06 12 views
23

Me gustaría implementar algún tipo de patrón singleton en mi programa Python. Estaba pensando en hacerlo sin usar clases; es decir, me gustaría poner todas las funciones y variables relacionadas con singleton dentro de un módulo y considerarlo como un singleton real.Python: pensando en un módulo y sus variables como singleton - ¿Enfoque limpio?

Por ejemplo, dicen que esto es estar en el 'singleton_module.py' file:

# singleton_module.py 

# Singleton-related variables 
foo = 'blah' 
bar = 'stuff' 

# Functions that process the above variables 
def work(some_parameter): 
    global foo, bar 
    if some_parameter: 
     bar = ... 
    else: 
     foo = ... 

A continuación, el resto del programa (es decir, otros módulos) sería utilizar este producto único de este modo:

# another_module.py 

import singleton_module 

# process the singleton variables, 
# which changes them across the entire program 
singleton_module.work(...) 

# freely access the singleton variables 
# (at least for reading) 
print singleton_module.foo 

Esto parece ser una buena idea para mí, ya que se ve bastante limpio en los módulos que utilizan el singlete en.

Sin embargo, todas estas afirmaciones "globales" tediosas en el módulo singleton son feas. Ocurren en cada función que procesa las variables relacionadas con singleton. Eso no es mucho en este ejemplo en particular, pero cuando tienes más de 10 variables para administrar en varias funciones, no es bonito.

Además, esto es bastante propenso a errores si olvida los enunciados globales: se crearán variables locales para la función, y las variables del módulo no se modificarán, ¡que no es lo que quiere!

Entonces, ¿esto se consideraría limpio? ¿Existe un enfoque similar al mío que logra eliminar el desastre "global"?

¿O simplemente no es el camino a seguir?

+0

¿Por qué necesita un singleton en primer lugar. Parece que el 90% de las personas que piensan que quieren/necesitan un singleton realmente no. – delnan

+0

Bueno, por supuesto que realmente * no necesito * un singleton. Solía ​​usar una clase y funcionó bien, pero pensé, ya que solo tengo una instancia de esta clase en particular, ¿no podría convertirla en singleton? –

+0

Bueno, dado que ya descubrió un problema con el singleton, yo diría que la clase gana;) – delnan

Respuesta

17

Una alternativa común a la utilización de un módulo como un conjunto unitario es de Borg pattern Alex Martelli:

class Borg: 
    __shared_state = {} 
    def __init__(self): 
     self.__dict__ = self.__shared_state 
    # and whatever else you want in your class -- that's all! 

No puede haber varias instancias de esta clase, pero todos comparten el mismo estado.

+1

[Alex] (http://stackoverflow.com/users/95810/alex-martelli) parece han desaparecido de SO - ¡no se lo ha visto desde noviembre pasado! 8v ( –

+0

) ¿Quiere decir variables múltiples que se refieren a la misma instancia o múltiples instancias que comparten el mismo estado? ¿Puede aclarar? – Jaydev

+1

@Jaydev Me refiero a lo que dije: varias instancias comparten el mismo estado.El diccionario '__shared_state' es una variable de clase, por lo que todas las instancias de la clase comparten su estado. –

0

Al igual que con la sugerencia de "patrón Borg" de Sven, podría mantener todos los datos de estado en una clase, sin crear instancias de la clase. Este método utiliza clases de estilo nuevo, creo.

Este método podría incluso ser adaptado en el patrón de Borg, con la advertencia de que la modificación de los estados miembros de las instancias de la clase requeriría el acceso al atributo __class__ de la instancia (instance.__class__.foo = 'z' en lugar de instance.foo = 'z', aunque se puede también acaba de hacer stateclass.foo = 'z').

class State: # in some versions of Python, may need to be "class State():" or "class State(object):" 
    __slots__ = [] # prevents additional attributes from being added to instances and same-named attributes from shadowing the class's attributes 
    foo = 'x' 
    bar = 'y' 

    @classmethod 
    def work(cls, spam): 
     print(cls.foo, spam, cls.bar) 

Tenga en cuenta que las modificaciones a los atributos de la clase se reflejarán en las instancias de la clase, incluso después de la creación de instancias. Esto incluye agregar nuevos atributos y eliminar los existentes, lo que podría tener algunos efectos interesantes y posiblemente útiles (aunque también puedo ver cómo eso podría causar problemas en algunos casos). Pruébelo usted mismo.

3

Quizás pueda poner todas las variables en un dict global, y puede usar directamente el dict en sus funciones sin "global".

# Singleton-related variables 
my_globals = {'foo': 'blah', 'bar':'stuff'} 

# Functions that process the above variables 
def work(some_parameter): 
    if some_parameter: 
     my_globals['bar'] = ... 
    else: 
     my_globals['foo'] = ... 

por qué se puede hacer así es Python Scopes and Namespaces.

0

Un enfoque para la implementación de un patrón singleton con Python también puede ser:

tienen Singleton __init()__ método lanza una excepción si una instancia de la clase ya existe. Más precisamente, la clase tiene un miembro _single. Si este miembro es diferente de None, se genera una excepción.

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

Se podría argumentar que el manejo de la creación de instancia singleton con excepciones tampoco es muy limpio. Podemos ocultar los detalles de implementación con un método handle() como en

def Handle(x = Singleton): 
    try: 
     single = x() 
    except Singleton, s: 
     single = s 
    return single 

este método Handle() es muy similar a lo que sería una aplicación C++ del patrón Singleton. Podríamos tener en Singleton clase del handle()

Singleton& Singleton::Handle() { 

    if(!psingle) { 
    psingle = new Singleton; 
    } 
    return *psingle; 
} 

regresar ya sea un nuevo Singleton instancia o una referencia a la instancia de la clase única existente Singleton.

Manejo de toda la jerarquía

Si Single1 y Single2 clases derivan de Singleton, una única instancia de Singleton través de uno de existe la clase derivada. Esto puede ser verificar con esto:

>>> child = S2('singlething') 
>>> junior = Handle(S1) 
>>> junior.name() 
'singlething' 
0

edificio fuera de WillYang's answer y tomando un paso más allá de la limpieza: definir una clase simple para mantener su diccionario global para que sea más fácil hacer referencia a:

class struct(dict): 
    def __init__(self, **kwargs): 
     dict.__init__(self, kwargs) 
     self.__dict__ = self 

g = struct(var1=None, var2=None) 

def func(): 
    g.var1 = dict() 
    g.var3 = 10 
    g["var4"] = [1, 2] 
    print(g["var3"]) 
    print(g.var4) 

Al igual que antes de poner lo que desea en g, pero ahora está súper limpio. :)

0

Para un Singleton legítima:

class SingletonMeta(type): 
    __classes = {} # protect against defining class with the same name 
    def __new__(cls, cls_name, cls_ancestors, cls_dict): 
     if cls_name in cls.__classes: 
      return cls.__classes[cls_name] 
     type_instance = super(SingletonMeta, cls).__new__(cls, cls_name, cls_ancestors, cls_dict) # pass 'type' instead of 'cls' if you dont want SingletonMeta's attributes reflected in the class 
     return type_instance() # call __init__ 

class Singleton: 
    __metaclass__ = SingletonMeta 
    # define __init__ however you want 

    __call__(self, *args, *kwargs): 
     print 'hi!' 

ver que realmente es un producto único, tratar de crear una instancia de esta clase, o cualquier clase que hereda de ella.

singleton = Singleton() # prints "hi!" 
Cuestiones relacionadas