杰瑞科技汇

Supervidor Python是什么?有什么用?

¡Claro! El término "supervidor" en Python se refiere a un patrón de diseño (design pattern) muy útil, también conocido como "Observer" en inglés. Es fundamental para crear sistemas desacoplados donde un objeto (el "sujeto" o "supervisado") necesita notificar automáticamente a otros objetos (los "observadores" o "supervisores") sobre cambios en su estado.

Imagina una suscripción a un canal de YouTube:

  • Canal de YouTube (Sujeto/Subject): Es el objeto principal que tiene un estado (nuevos videos, cambios en la descripción, etc.).
  • Tus Suscriptores (Observers): Son los objetos que quieren ser notificados cuando el canal sube un nuevo video.
  • Notificación: Cuando el canal sube un video, notifica automáticamente a todos sus suscriptores.

En este patrón, el canal no necesita saber quiénes son los suscriptores, solo que debe notificar a todos los que se han registrado. Esto crea un bajo acoplamiento.


Componentes del Patrón Supervisor (Observer)

  1. Sujeto (Subject / Observable): Es la interfaz o clase abstracta que define los métodos para gestionar los observadores.

    • attach(observer): Permite a un observador suscribirse.
    • detach(observer): Permite a un observador darse de baja.
    • notify(): Notifica a todos los observadores registrados sobre un cambio de estado.
  2. Observador (Observer): Es la interfaz o clase abstracta que define el método que será llamado por el sujeto.

    • update(subject): Es el método que el observador implementa para reaccionar a la notificación.
  3. Sujeto Concreto (Concrete Subject): Es la clase que implementa la lógica del sujeto. Mantiene una lista de observadores y su estado. Cuando su estado cambia, invoca el método notify().

  4. Observador Concreto (Concrete Observer): Es la clase que implementa la lógica del observador. Define qué acción debe realizar cuando recibe una notificación.


Ejemplo Práctico en Python

Vamos a implementar el ejemplo del "Clima" que es clásico para este patrón.

  • Sujeto Concreto (DatosClima): Un objeto que recibe datos de temperatura, humedad y presión.
  • Observadores Concretos (DisplayCondicionesActuales, DisplayEstadisticas, ForecastDisplay): Pantallas que muestran información diferente basada en los datos del clima.

Paso 1: Definir las Interfaces (Clases Base)

Primero, creamos las clases base para el Sujeto y el Observador.

from abc import ABC, abstractmethod
# Observador: Define la interfaz de actualización para los objetos que deben ser notificados.
class Observador(ABC):
    @abstractmethod
    def actualizar(self, temperatura, humedad, presion):
        pass
# Sujeto: Mantiene una lista de observadores y los notifica de los cambios.
class Sujeto(ABC):
    @abstractmethod
    def registrar_observador(self, observador: Observador):
        pass
    @abstractmethod
    def eliminar_observador(self, observador: Observador):
        pass
    @abstractmethod
    def notificar_observadores(self):
        pass

Paso 2: Implementar el Sujeto Concreto

Esta clase guardará los datos del clima y notificará a los observadores cuando cambien.

# Sujeto Concreto: Implementa la lógica del Sujeto.
class DatosClima(Sujeto):
    def __init__(self):
        self._observadores = []
        self._temperatura = 0.0
        self._humedad = 0.0
        self._presion = 1013.2
    def registrar_observador(self, observador: Observador):
        self._observadores.append(observador)
    def eliminar_observador(self, observador: Observador):
        # La forma más segura es manejar el caso en que el observador no esté
        if observador in self._observadores:
            self._observadores.remove(observador)
    def notificar_observadores(self):
        for observador in self._observadores:
            observador.actualizar(self._temperatura, self._humedad, self._presion)
    # Métodos para cambiar el estado y notificar automáticamente
    def medir_cambios(self, temperatura, humedad, presion):
        print("\nActualizando mediciones del clima...")
        self._temperatura = temperatura
        self._humedad = humedad
        self._presion = presion
        self.notificar_observadores()

Paso 3: Implementar los Observadores Concretos

Cada observador reaccionará a la notificación de una manera diferente.

# Observador Concreto 1: Muestra las condiciones actuales.
class DisplayCondicionesActuales(Observador):
    def actualizar(self, temperatura, humedad, presion):
        print("--- Condiciones Actuales ---")
        print(f"Temperatura: {temperatura}°C")
        print(f"Humedad: {humedad}%")
        print("---------------------------")
# Observador Concreto 2: Muestra estadísticas simples.
class DisplayEstadisticas(Observador):
    def __init__(self):
        self._temperaturas = []
    def actualizar(self, temperatura, humedad, presion):
        self._temperaturas.append(temperatura)
        if len(self._temperaturas) > 5: # Mantener solo las últimas 5
            self._temperaturas.pop(0)
        promedio = sum(self._temperaturas) / len(self._temperaturas)
        print("--- Estadísticas de Temperatura ---")
        print(f"Temperatura Promedio (últimas 5 lecturas): {promedio:.2f}°C")
        print("------------------------------------")
# Observador Concreto 3: Un pronóstico simple.
class ForecastDisplay(Observador):
    def __init__(self):
        self._prev_temp = 0.0
    def actualizar(self, temperatura, humedad, presion):
        self._prev_temp = temperatura
        if temperatura > self._prev_temp:
            pronostico = "Mejorando: ¡El clima se calentará!"
        elif temperatura < self._prev_temp:
            pronostico = "Atención: ¡El clima se enfriará!"
        else:
            pronostico = "Sin cambios: El clima se mantendrá estable."
        print("--- Pronóstico ---")
        print(pronostico)
        print("-----------------")

Paso 4: Poner Todo Junto (El Programa Principal)

Ahora, conectamos todo para que funcione.

if __name__ == "__main__":
    # 1. Crear el Sujeto (la fuente de datos)
    datos_clima = DatosClima()
    # 2. Crear los Observadores (las pantallas que mostrarán los datos)
    display_actuales = DisplayCondicionesActuales()
    display_estadisticas = DisplayEstadisticas()
    display_pronostico = ForecastDisplay()
    # 3. Registrar los observadores en el sujeto
    datos_clima.registrar_observador(display_actuales)
    datos_clima.registrar_observador(display_estadisticas)
    datos_clima.registrar_observador(display_pronostico)
    # 4. Simular cambios en los datos del clima
    # Los observadores serán notificados automáticamente
    datos_clima.medir_cambios(25.5, 65, 1015.1)
    datos_clima.medir_cambios(26.8, 70, 1014.5)
    datos_clima.medir_cambios(24.2, 90, 1016.3)
    # 5. Podemos eliminar un observador si ya no nos interesa
    print("\nEliminando el pronóstico de la lista de notificaciones...")
    datos_clima.eliminar_observador(display_pronostico)
    # 6. Realizar otro cambio para ver que el pronóstico ya no se actualiza
    datos_clima.medir_cambios(23.1, 85, 1017.0)

Salida del Programa

Actualizando mediciones del clima...
--- Condiciones Actuales ---
Temperatura: 25.5°C
Humedad: 65%
---------------------------
--- Estadísticas de Temperatura ---
Temperatura Promedio (últimas 5 lecturas): 25.50°C
------------------------------------
--- Pronóstico ---
Mejorando: ¡El clima se calentará!
-----------------
Actualizando mediciones del clima...
--- Condiciones Actuales ---
Temperatura: 26.8°C
Humedad: 70%
---------------------------
--- Estadísticas de Temperatura ---
Temperatura Promedio (últimas 5 lecturas): 26.15°C
------------------------------------
--- Pronóstico ---
Mejorando: ¡El clima se calentará!
-----------------
Actualizando mediciones del clima...
--- Condiciones Actuales ---
Temperatura: 24.2°C
Humedad: 90%
---------------------------
--- Estadísticas de Temperatura ---
Temperatura Promedio (últimas 5 lecturas): 25.50°C
------------------------------------
--- Pronóstico ---
Atención: ¡El clima se enfriará!
-----------------
Eliminando el pronóstico de la lista de notificaciones...
Actualizando mediciones del clima...
--- Condiciones Actuales ---
Temperatura: 23.1°C
Humedad: 85%
---------------------------
--- Estadísticas de Temperatura ---
Temperatura Promedio (últimas 5 lecturas): 24.70°C
------------------------------------

Ventajas del Patrón Supervisor/Observer

  • Desacoplamiento: El Sujeto no conoce los detalles de los Observadores. Solo sabe que existen y que deben ser notificados. Esto facilita la modificación y adición de nuevos observadores sin tocar el código del sujeto.
  • Comunicación Broadcast: Permite una comunicación de uno-a-muchos de forma eficiente.
  • Separación de Responsabilidades: Cada objeto tiene una única responsabilidad bien definida.

Implementación Integrada en Python (sin abc)

Para casos más simples, no es necesario usar las clases abstractas (ABC). Python es muy dinámico y podemos lograr el mismo resultado de una manera más "pythónica".

class SujetoSimple:
    def __init__(self):
        self._observadores = []
    def registrar(self, observador):
        if observador not in self._observadores:
            self._observadores.append(observador)
    def eliminar(self, observador):
        try:
            self._observadores.remove(observador)
        except ValueError:
            pass
    def notificar(self, *args, **kwargs):
        for observador in self._observadores:
            observador(*args, **kwargs)
class ObservadorSimple:
    def __init__(self, nombre):
        self.nombre = nombre
    def __call__(self, mensaje):
        print(f"[{self.nombre}] Recibió: {mensaje}")
# Uso
sujeto = SujetoSimple()
obs1 = ObservadorSimple("Notificación 1")
obs2 = ObservadorSimple("Alerta de Sistema")
sujeto.registrar(obs1)
sujeto.registrar(obs2)
sujeto.notificar("¡El sistema está operativo!")
# Salida:
# [Notificación 1] Recibió: ¡El sistema está operativo!
# [Alerta de Sistema] Recibió: ¡El sistema está operativo!
sujeto.eliminar(obs1)
sujeto.notificar("Advertencia: sobrecarga detectada.")
# Salida:
# [Alerta de Sistema] Recibió: Advertencia: sobrecarga detectada.

¿Dónde se usa este patrón en Python?

Aunque no siempre se le llame "supervisor", el concepto está por todas partes:

  • Sistemas de Eventos: GUI (PyQt, Tkinter), frameworks web (Django Signals), donde un evento (clic de un botón) notifica a una función controladora.
  • Patrones MVC (Modelo-Vista-Controlador): El Modelo (sujeto) notifica a las Vistas (observadores) cuando los datos cambian.
  • Patrones MVVM (Modelo-Vista-ViewModel): Similar a MVC, el ViewModel notifica a la Vista sobre cambios.
  • Bibliotecas de Logging: Un logger (sujeto) puede enviar logs a múltiples handlers (observadores) como consola, archivo, etc.

En resumen, el patrón supervisor (observer) es una herramienta poderosa para construir sistemas reactivos, flexibles y fáciles de mantener en Python.

分享:
扫描分享到社交APP
上一篇
下一篇