Estoy creando una aplicación de 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, los Singletons deben evitarse en Python y, por lo tanto, tengo curiosidad sobre cuál es la mejor práctica para este tipo de problema.
Las siguientes son tres formas de usar singleton en Python. Usando metaclass y decorator para alcanzar la meta.
usar __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 usar __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 usar decorator
def singleton(cls, *args, **kw): instances = {} def _singleton(*args, **kw): 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 .
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 dificultan las pruebas, 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 detalles sobre por qué los globales son generalmente una mala práctica)
En su escenario particular, usar un singleton probablemente sea apropiado porque simplemente está tratando de evitar que SocketCommunication se inicialice varias veces (por una buena razón), en lugar de intentar usarlo como un contenedor para un estado global.
Ver ¿Son realmente tan malos los solteros? y ¿Qué tienen de malo los singletons? para una discusión sobre Singletons.