Estoy creando una aplicación Python que incluye comunicación de socket con un servidor. Me gustaría tener un módulo que pueda usarse en toda mi aplicación (varios otros módulos). Actualmente mi módulo se ve así:

class SocketCommunication:

    def __init__(self):
        self.socketIO = SocketIO(settings.ADDRESS, settings.PORT, Namespace)

    def emit(self, message, data):
        json_data = json.dumps(data.__dict__)
        self.socketIO.emit(message, json_data)


class Namespace(BaseNamespace):
    def on_connect(self):
        print '[Connected]'

    def on_disconnect(self):
        print "[Disconnected]"

Cuando uso esto en otros módulos hago lo siguiente:

import SocketCommunication
self.sc = SocketCommunication()

El problema es que cada vez que hago esto, se crea una nueva conexión que se mostrará como un nuevo cliente en el servidor, y eso no es deseable. Por lo que puedo leer, se deben evitar los Singletons en Python y, por lo tanto, tengo curiosidad sobre cuál es la mejor práctica para este tipo de problema.

8
user3170354 15 feb. 2017 a las 01:55

2 respuestas

La mejor respuesta

Las siguientes son tres formas de usar singleton en Python. Usando metaclass y decorator para alcanzar la meta.

  1. utilizar __new__

    class Singleton(object):
        def __new__(cls, *args, **kw):
            if not hasattr(cls, '_instance'):
                orig = super(Singleton, cls)
                cls._instance = orig.__new__(cls, *args, **kw)
            return cls._instance
    
    class MyClass(Singleton):
        a = 1
    
    one = MyClass()
    two = MyClass()
    
    two.a = 3
    print one.a
    #3
    print id(one)
    #29097904
    print id(two)
    #29097904
    print one == two
    #True
    print one is two
    #True
    
  2. utilizar __metaclass__

    class Singleton2(type):
        def __init__(cls, name, bases, dict):
            super(Singleton2, cls).__init__(name, bases, dict)
            cls._instance = None
    
    def __call__(cls, *args, **kw):
        if cls._instance is None:
            cls._instance = super(Singleton2, cls).__call__(*args, **kw)
        return cls._instance
    
    
    class MyClass2(object):
        __metaclass__ = Singleton2
    
    one = MyClass2()
    two = MyClass2()
    
    two.a = 3
    print one.a
    #3
    print id(one)
    #31495472
    print id(two)
    #31495472
    print one == two
    #True
    print one is two
    #True
    
  3. utilizar decorator

     def singleton(cls, *args, **kw):
        instances = {}
        def _singleton():
           if cls not in instances:
                instances[cls] = cls(*args, **kw)
           return instances[cls]
        return _singleton
    
    @singleton
    class MyClass3(object):
        a = 1
        def __init__(self, x=0):
            self.x = x
    
    one = MyClass3()
    two = MyClass3()
    
    two.a = 3
    print one.a
    #3
    print id(one)
    #29660784
    print id(two)
    #29660784
    print one == two
    #True
    print one is two
    #True
    one.x = 1
    print one.x
    #1
    print two.x
    #1
    

Prefiero usar decorator.

9
caimaoy 15 feb. 2017 a las 02:31

Los singletons son controvertidos porque a menudo se usan como una forma de envolver variables globales. Es por eso que algunas personas abogan por su evitación. Los globales hacen que las pruebas sean más difíciles, limitan el control de acceso y a menudo conducen a un fuerte acoplamiento entre las variables. (consulte http://wiki.c2.com/?GlobalVariablesAreBad para obtener más información detalles de por qué los globales son generalmente una mala práctica)

En su escenario particular, lo más probable es que usar un singleton sea apropiado porque simplemente está tratando de evitar que SocketCommunication se inicialice varias veces (por una buena razón), en lugar de tratar de usarlo como un contenedor para un estado global.

Consulta ¿Son los singleton realmente tan malos? y ¿Qué tienen de malo los singletons? para una discusión sobre Singletons.

0
Community 23 may. 2017 a las 12:16