Python自定义事件实现与应用

在 Python 中,实现自定义事件和事件发送主要依赖于面向对象编程和设计模式(如 观察者模式)。下面我将从基础实现到高级用法逐步讲解,并附上代码示例。


一、基础概念

事件驱动编程 的核心是:

  1. 事件 (Event):表示某种动作或状态变化(如鼠标点击、数据到达)
  2. 事件发送者 (Emitter):触发事件的对象
  3. 事件监听者 (Listener):订阅并处理事件的对象

二、基础实现(不使用第三方库)

1. 简单事件管理器类

class EventManager:
    def __init__(self):
        self._event_handlers = {}

    def register_event(self, event_name, handler):
        """注册事件监听器"""
        if event_name not in self._event_handlers:
            self._event_handlers[event_name] = []
        self._event_handlers[event_name].append(handler)

    def trigger_event(self, event_name, *args, **kwargs):
        """触发事件"""
        handlers = self._event_handlers.get(event_name, [])
        for handler in handlers:
            handler(*args, **kwargs)

# 使用示例
def on_click(x, y):
    print(f"Clicked at ({x}, {y})")

manager = EventManager()
manager.register_event("click", on_click)
manager.trigger_event("click", 100, 200)  # 输出: Clicked at (100, 200)

2. 带参数的事件对象

class Event:
    def __init__(self, name, **kwargs):
        self.name = name
        self.data = kwargs

class AdvancedEventManager:
    def __init__(self):
        self._listeners = {}

    def add_listener(self, event_name, callback):
        self._listeners.setdefault(event_name, []).append(callback)

    def dispatch(self, event):
        for callback in self._listeners.get(event.name, []):
            callback(event)

# 使用示例
def handle_login(event):
    print(f"User {event.data['username']} logged in")

manager = AdvancedEventManager()
manager.add_listener("user_login", handle_login)

login_event = Event("user_login", username="john", ip="192.168.1.1")
manager.dispatch(login_event)  # 输出: User john logged in

三、使用第三方库(推荐)

1. 使用 PyPubSub

安装库:

pip install pypubsub

示例代码:

from pubsub import pub

# 定义监听函数
def on_data_received(data):
    print(f"Received data: {data}")

# 订阅事件
pub.subscribe(on_data_received, 'data_channel')

# 发送事件
pub.sendMessage('data_channel', data=[1, 2, 3])

# 带多个参数
def on_multi_params(name, age):
    print(f"{name} is {age} years old")

pub.subscribe(on_multi_params, 'user_info')
pub.sendMessage('user_info', name="Alice", age=25)

2. 使用 blinker(Flask 使用的基础库)

安装:

pip install blinker

示例:

from blinker import Signal

# 创建信号
data_ready = Signal('data-ready')

# 注册接收器
@data_ready.connect
def handle_data(sender, **kwargs):
    print(f"Data from {sender}: {kwargs['data']}")

# 发送信号
data_ready.send('server1', data={'temp': 36.5})

四、高级技巧

1. 异步事件处理

结合 asyncio 实现异步事件:

import asyncio

class AsyncEventManager:
    def __init__(self):
        self._events = {}

    def register(self, event_name, coro):
        self._events.setdefault(event_name, []).append(coro)

    async def trigger(self, event_name, *args):
        coros = self._events.get(event_name, [])
        await asyncio.gather(*(coro(*args) for coro in coros))

# 使用示例
async def send_email(user):
    print(f"Sending email to {user}...")
    await asyncio.sleep(1)

manager = AsyncEventManager()
manager.register("user_registered", send_email)

async def main():
    await manager.trigger("user_registered", "alice@example.com")

asyncio.run(main())

2. 类型化事件(Python 3.12+ 使用类型标注)

from typing import Protocol, Any

class Event(Protocol):
    name: str
    data: dict[str, Any]

class UserLoginEvent:
    def __init__(self, username: str):
        self.name = "user_login"
        self.data = {"username": username}

def handle_user_login(event: UserLoginEvent):
    print(f"Handle login: {event.data['username']}")

# 类型检查工具会验证事件类型

五、应用场景

  1. GUI 开发(按钮点击事件)
  2. 游戏开发(角色受伤事件)
  3. 微服务通信(消息通知)
  4. 插件系统(扩展点事件)

六、设计原则

  1. 松耦合:事件发送者不需要知道监听者的存在
  2. 单一职责:每个事件处理函数只做一件事
  3. 可扩展性:方便添加新事件类型和处理程序
  4. 错误隔离:某个事件处理失败不应影响其他处理程序

根据项目复杂度选择实现方式:小型项目可以用基础实现,大型项目推荐使用 PyPubSubblinker 等成熟库。

posted @ 2025-05-19 15:36  天堂面包  阅读(146)  评论(0)    收藏  举报