Menu
Coddy logo textTech

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 class has only one instance y proporciona un punto global de access a ella. Esto es útil para recursos como conexiones de database o settings de configuración.

Aquí hay 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._instance

El método __new__ controla la creación del objeto. Comprueba si ya existe una instancia antes de crear una nueva.

Crea dos instancias de la clase Singleton:

singleton1 = Singleton()
singleton2 = Singleton()

Verifica 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 memoria

Aquí hay un ejemplo más 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"
            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 administrador 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ón

Salida:

True
140234567890123
140234567890123
Creating new database connection
Connected to MySQL database
Connected to MySQL database
Executing: SELECT * FROM users
True
True

Punto clave: El patrón Singleton utiliza __new__ para controlar la creación de object, garantizando que solo exista one instance. Úselo para recursos que deben tener only one copia en toda su aplicación, como conexiones de database, registradores o administradores de configuración. Recuerde que todas las variables que apuntan a un Singleton hacen referencia al same object en memory.

challenge icon

Desafío

Fácil

En este desafío, implementarás el patrón de diseño Singleton para una clase de conexión a base de datos.

  • Edita databaseconnection.py para implementar el patrón Singleton siguiendo los comentarios TODO
  • El archivo driver.py contiene amplios escenarios de prueba y no debe ser modificado

Pruébalo tú mismo

from databaseconnection import DatabaseConnection

# Manejador integral de casos de prueba
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()  # Should print "Connected to database at localhost"

elif test_case == "disconnect_message":
    db = DatabaseConnection()
    db.connect()
    db.disconnect()  # Should print "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()  # Will still print "Connected to database at localhost"
    # El método connect tiene un mensaje codificado de forma fija que no usa el atributo host
    print("Message still shows localhost: True")
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming