Python

"-"


1. (Subject)

2. (Concrete Subject)

3. (Observer)

4. (Concrete Observer)


from abc import ABC, abstractmethod
from typing import List

#
class Observer(ABC):
    @abstractmethod
    def update(self, message: str):
        """"""
        pass

#
class Subject(ABC):
    @abstractmethod
    def register_observer(self, observer: Observer):
        """"""
        pass
   
    @abstractmethod
    def remove_observer(self, observer: Observer):
        """"""
        pass
   
    @abstractmethod
    def notify_observers(self):
        """"""
        pass

# -
class NewsPublisher(Subject):
    def __init__(self):
        self._observers: List[Observer] = []
        self._latest_news = ""
   
    def register_observer(self, observer: Observer):
        """"""
        if observer not in self._observers:
            self._observers.append(observer)
            print(f" {observer.__class__.__name__} ")
   
    def remove_observer(self, observer: Observer):
        """"""
        if observer in self._observers:
            self._observers.remove(observer)
            print(f" {observer.__class__.__name__} ")
   
    def notify_observers(self):
        """"""
        print(f"\n: {self._latest_news}")
        for observer in self._observers:
            observer.update(self._latest_news)
   
    def publish_news(self, news: str):
        """"""
        self._latest_news = news
        self.notify_observers()
   
    def get_observer_count(self) -> int:
        """"""
        return len(self._observers)

# -
class EmailSubscriber(Observer):
    def __init__(self, name: str):
        self.name = name
   
    def update(self, message: str):
        """"""
        print(f"[] {self.name} : {message}")

# -
class SMSSubscriber(Observer):
    def __init__(self, name: str, phone: str):
        self.name = name
        self.phone = phone
   
    def update(self, message: str):
        """"""
        print(f"[] {self.phone} - {self.name} : {message}")

# -
class AppPushSubscriber(Observer):
    def __init__(self, username: str):
        self.username = username
   
    def update(self, message: str):
        """"""
        print(f"[] {self.username} : {message}")

def demo_basic_observer():
    """"""
    print("=== ===\n")
   
    #
    publisher = NewsPublisher()
   
    #
    email_user = EmailSubscriber("")
    sms_user = SMSSubscriber("", "13800138000")
    app_user = AppPushSubscriber("")
   
    #
    publisher.register_observer(email_user)
    publisher.register_observer(sms_user)
    publisher.register_observer(app_user)
   
    print(f"\n: {publisher.get_observer_count()}")
   
    #
    publisher.publish_news("Python 3.12 ")
   
    #
    publisher.remove_observer(sms_user)
   
    print(f"\n: {publisher.get_observer_count()}")
   
    #
    publisher.publish_news("")

#
if __name__ == "__main__":
    demo_basic_observer()

===  ===

 EmailSubscriber 
 SMSSubscriber 
 AppPushSubscriber 

: 3

: Python 3.12 
[]  : Python 3.12 
[] 13800138000 -  : Python 3.12 
[]   : Python 3.12 
 SMSSubscriber 

: 2

: 
[]  : 
[]   : 

#
class Stock:
    """"""
    def __init__(self, symbol: str, price: float):
        self.symbol = symbol  #
        self._price = price   #
        self._history = [price]  #
   
    @property
    def price(self) -> float:
        return self._price
   
    @price.setter
    def price(self, value: float):
        """"""
        old_price = self._price
        self._price = value
        self._history.append(value)
        return old_price, value
   
    def get_price_change(self) -> float:
        """"""
        if len(self._history) < 2:
            return 0.0
        return ((self._history[-1] - self._history[-2]) / self._history[-2]) * 100

class StockMarket(Subject):
    """ - """
    def __init__(self):
        self._observers: List[Observer] = []
        self._stocks = {}  # : {: Stock}
   
    def register_observer(self, observer: Observer):
        if observer not in self._observers:
            self._observers.append(observer)
   
    def remove_observer(self, observer: Observer):
        if observer in self._observers:
            self._observers.remove(observer)
   
    def notify_observers(self, stock: Stock):
        """"""
        change = stock.get_price_change()
        message = f"{stock.symbol}: ${stock.price:.2f} ({change:+.2f}%)"
       
        for observer in self._observers:
            observer.update(message)
   
    def add_stock(self, symbol: str, initial_price: float):
        """"""
        self._stocks[symbol] = Stock(symbol, initial_price)
   
    def update_stock_price(self, symbol: str, new_price: float):
        """"""
        if symbol in self._stocks:
            stock = self._stocks[symbol]
            old_price, current_price = stock.price, new_price
            stock.price = new_price
           
            #
            if old_price != current_price:
                self.notify_observers(stock)

class Investor(Observer):
    """ - """
    def __init__(self, name: str, interest_stocks: List[str] = None):
        self.name = name
        self.interest_stocks = interest_stocks or []
        self.notifications = []
   
    def update(self, message: str):
        """"""
        stock_symbol = message.split(":")[0]
       
        #
        if not self.interest_stocks or stock_symbol in self.interest_stocks:
            self.notifications.append(message)
            print(f" {self.name} : {message}")
   
    def show_notifications(self):
        """"""
        print(f"\n=== {self.name} ===")
        for notification in self.notifications:
            print(f"  - {notification}")

def demo_stock_market():
    """"""
    print("\n=== ===\n")
   
    #
    market = StockMarket()
   
    #
    market.add_stock("AAPL", 150.0)
    market.add_stock("GOOGL", 2800.0)
    market.add_stock("TSLA", 250.0)
   
    #
    investor1 = Investor("", ["AAPL", "GOOGL"])
    investor2 = Investor("", ["TSLA"])
    investor3 = Investor("")
   
    #
    market.register_observer(investor1)
    market.register_observer(investor2)
    market.register_observer(investor3)
   
    #
    print("...")
    market.update_stock_price("AAPL", 152.5)   # AAPL
    market.update_stock_price("TSLA", 245.0)   # TSLA
    market.update_stock_price("GOOGL", 2820.0) # GOOGL
    market.update_stock_price("AAPL", 151.0)   # AAPL
   
    #
    investor1.show_notifications()
    investor2.show_notifications()
    investor3.show_notifications()

#
demo_stock_market()


1. GUI

#
button.on_click(handler1)  #
button.on_click(handler2)  #
#

2.

3.

4.

  • UI

1.

import weakref

class SafeSubject(Subject):
    def __init__(self):
        self._observers = weakref.WeakSet()  #
   
    def register_observer(self, observer: Observer):
        self._observers.add(observer)

2.

import asyncio

class AsyncObserver(ABC):
    @abstractmethod
    async def update_async(self, message: str):
        pass

class AsyncSubject:
    async def notify_observers_async(self):
        tasks = [observer.update_async(self._message)
                for observer in self._observers]
        await asyncio.gather(*tasks)

3.

def safe_notify(self):
    for observer in self._observers.copy():  #
        try:
            observer.update(self._message)
        except Exception as e:
            print(f" {observer} : {e}")

Python