Patrón Singleton
Parte de la sección Object Oriented Programming del Journey de Python de Coddy — lección 45 de 64.
El patrón Singleton garantiza que una clase tenga una única instancia y proporciona un punto de acceso global a ella. Esto es útil para recursos como conexiones a bases de datos o ajustes de configuración.
Aquí tienes una implementación básica de Singleton:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceEl método __new__ controla la creación de objetos. Comprueba si ya existe una instancia antes de crear una nueva.
Crea dos instancias de la clase Singleton:
singleton1 = Singleton()
singleton2 = Singleton()Comprueba si ambas variables hacen referencia al mismo objeto:
print(singleton1 is singleton2) # True
print(id(singleton1)) # Misma dirección de memoria
print(id(singleton2)) # Misma dirección de memoriaAquí hay un ejemplo más práctico con una conexión a una base de datos:
class DatabaseConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connection = "Connected to MySQL database"
print("Creating new database connection")
return cls._instance
def query(self, sql):
return f"Executing: {sql}"
# El primer acceso crea la conexión
db1 = DatabaseConnection()
print(db1.connection)
# El segundo acceso reutiliza la misma conexión
db2 = DatabaseConnection()
print(db2.connection)
print(db1.query("SELECT * FROM users"))
print(db1 is db2)Crea un gestor de configuración usando Singleton:
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.settings = {}
return cls._instance
def set_setting(self, key, value):
self.settings[key] = value
def get_setting(self, key):
return self.settings.get(key)
config1 = Config()
config1.set_setting("debug", True)
config2 = Config()
print(config2.get_setting("debug")) # True - misma configuraciónSalida:
True
140234567890123
140234567890123
Creating new database connection
Connected to MySQL database
Connected to MySQL database
Executing: SELECT * FROM users
True
TruePunto clave: El patrón Singleton utiliza __new__ para controlar la creación de objetos, asegurando que solo exista una instancia. Úsalo para recursos que deban tener una sola copia en toda tu aplicación, como conexiones a bases de datos, registradores (loggers) o gestores de configuración. Recuerda que todas las variables que apuntan a un Singleton hacen referencia al mismo objeto en memoria.
Desafío
FácilEn este desafío, implementarás el patrón de diseño Singleton para una clase de conexión a base de datos.
- Edita
databaseconnection.pypara implementar el patrón Singleton siguiendo los comentarios TODO - El archivo
driver.pycontiene escenarios de prueba exhaustivos y no debe ser modificado
Hoja de referencia
El patrón Singleton asegura que una clase tenga solo una instancia y proporciona un punto de acceso global a ella.
Implementación básica de Singleton usando __new__:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceEl método __new__ controla la creación de objetos verificando si ya existe una instancia antes de crear una nueva.
Probando instancias de Singleton:
singleton1 = Singleton()
singleton2 = Singleton()
print(singleton1 is singleton2) # True - mismo objetoEjemplo práctico con una conexión a base de datos:
class DatabaseConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connection = "Connected to MySQL database"
return cls._instance
def query(self, sql):
return f"Executing: {sql}"Ejemplo de gestor de configuración:
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.settings = {}
return cls._instance
def set_setting(self, key, value):
self.settings[key] = value
def get_setting(self, key):
return self.settings.get(key)Use Singleton para recursos que deban tener una sola copia en toda su aplicación, como conexiones a bases de datos, registradores (loggers) o gestores de configuración.
Pruébalo tú mismo
from databaseconnection import DatabaseConnection
# Manejador de casos de prueba exhaustivo
test_case = input()
if test_case == "identity_check":
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # Debería imprimir True
elif test_case == "connect_state":
db = DatabaseConnection()
print(f"Initial connected state: {db.connected}") # Debería imprimir False
db.connect()
print(f"After connect: {db.connected}") # Debería imprimir True
elif test_case == "disconnect_state":
db = DatabaseConnection()
db.connect()
db.disconnect()
print(f"After disconnect: {db.connected}") # Debería imprimir False
elif test_case == "multiple_instances_same_state":
db1 = DatabaseConnection()
db1.connect()
db2 = DatabaseConnection()
print(f"db2 connected state: {db2.connected}") # Debería imprimir True
elif test_case == "host_value":
db = DatabaseConnection()
print(f"Default host: {db.host}") # Debería imprimir "localhost"
db.host = "new-server"
db2 = DatabaseConnection()
print(f"New instance host: {db2.host}") # Debería imprimir "new-server"
elif test_case == "init_once":
db1 = DatabaseConnection()
db1.host = "custom-host"
db2 = DatabaseConnection()
print(f"Both instances have same host: {db1.host == db2.host}") # Debería imprimir True
print(f"Host value: {db1.host}") # Debería imprimir "custom-host"
elif test_case == "connect_message":
db = DatabaseConnection()
db.connect() # Debería imprimir "Connected to database at localhost"
elif test_case == "disconnect_message":
db = DatabaseConnection()
db.connect()
db.disconnect() # Debería imprimir "Disconnected from database"
elif test_case == "attribute_modification":
db1 = DatabaseConnection()
db1.port = 3306
db2 = DatabaseConnection()
print(f"db2 has port attribute: {hasattr(db2, 'port')}") # Debería imprimir True
print(f"db2 port value: {db2.port}") # Debería imprimir 3306
elif test_case == "reset_connection":
db1 = DatabaseConnection()
db1.connect()
print(f"Connected state: {db1.connected}") # Debería imprimir True
db2 = DatabaseConnection()
print(f"New instance connected state: {db2.connected}") # Debería imprimir True
db2.disconnect()
db3 = DatabaseConnection()
print(f"After disconnect, new instance state: {db3.connected}") # Debería imprimir False
elif test_case == "multiple_connects":
db = DatabaseConnection()
db.connect()
db.connect()
db.connect()
print(f"Connected state after multiple connects: {db.connected}") # Debería imprimir True
db.disconnect()
print(f"Connected state after disconnect: {db.connected}") # Debería imprimir False
elif test_case == "host_change_affects_message":
db = DatabaseConnection()
db.host = "custom-server"
db.connect() # Seguirá imprimiendo "Connected to database at localhost"
# El método connect tiene un mensaje predefinido que no utiliza el atributo host
print("Message still shows localhost: True")Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de la POO
Archivos externosIntroducción a la POOClases vs ObjetosEl parámetro selfMétodosAtributosMétodo constructor (__init__)Resumen - Calculadora simple4Herencia
Herencia básicaLa función super()Sobrescritura de métodosHerencia múltipleOrden de resolución de métodosResumen - Jerarquía de empleados7Métodos especiales
Introducción a los métodos mágicosSobrecarga de operadoresMétodos mágicos de contenedoresRepaso - Lista personalizada10Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy2Decoradores
Introducción a los decoradoresDecorador propertyDecorador de métodos estáticosDecorador de métodos de clase5Polimorfismo
Sobreescritura de métodos revisitadaDuck TypingClases y métodos abstractosDiseño de interfacesResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaMixinsMétodos estáticos y de claseDecoradores de claseGestores de contexto11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Propiedades de clase
Variables de instancia vs de claseDecoradores de propiedadesAtributos privadosResumen - Gestor de cuentas bancarias