15、用装饰器🧩 + 单例 + 反射写一个插件式系统

87 阅读2分钟

🧙‍♂️ 真正灵活的系统不是硬编码流程,而是通过插件动态扩展能力。本篇带你用 Python 实现一个「可插拔、可注册、可扩展」的轻量插件系统,顺便掌握三个高阶技术点:

  • 装饰器(注册)
  • 单例模式(全局调度)
  • 反射机制(动态加载)

✅ 本文目标

  • 实现一个 PluginManager 插件调度中心(单例)
  • 使用装饰器注册插件
  • 使用 importlib 实现动态加载
  • 支持:列出插件、调用插件、添加新插件无需改主程序

🧠 一、基础结构设计

# plugins/base.py
class BasePlugin:
    name = "base"

    def run(self, *args, **kwargs):
        raise NotImplementedError

🧰 二、插件注册装饰器 + 管理器(单例)

# plugin_manager.py
from plugins.base import BasePlugin

class PluginManager:
    _instance = None
    _plugins = {}

    def __new__(cls):
        if not cls._instance:
            cls._instance = super().__new__(cls)
        return cls._instance

    def register(self, plugin_cls):
        if not issubclass(plugin_cls, BasePlugin):
            raise TypeError("插件必须继承自 BasePlugin")
        self._plugins[plugin_cls.name] = plugin_cls()
        return plugin_cls  # 装饰器必须返回类

    def list_plugins(self):
        return list(self._plugins.keys())

    def get_plugin(self, name):
        return self._plugins.get(name)

🎯 三、编写插件(加装饰器)

# plugins/echo.py
from plugin_manager import PluginManager
from plugins.base import BasePlugin

@PluginManager().register
class EchoPlugin(BasePlugin):
    name = "echo"

    def run(self, text):
        print(f"[Echo插件] {text}")
# plugins/reverse.py
from plugin_manager import PluginManager
from plugins.base import BasePlugin

@PluginManager().register
class ReversePlugin(BasePlugin):
    name = "reverse"

    def run(self, text):
        print(f"[Reverse插件] {text[::-1]}")

🧪 四、主程序加载 + 调用插件

# main.py
import importlib
import os
from plugin_manager import PluginManager

def auto_import_plugins():
    for fname in os.listdir("plugins"):
        if fname.endswith(".py") and fname not in ("__init__.py", "base.py"):
            importlib.import_module(f"plugins.{fname[:-3]}")

def main():
    auto_import_plugins()

    pm = PluginManager()
    print("已加载插件:", pm.list_plugins())

    cmd = input("请输入插件名(如 echo/reverse):")
    text = input("请输入文本:")

    plugin = pm.get_plugin(cmd)
    if plugin:
        plugin.run(text)
    else:
        print("❌ 插件不存在")

if __name__ == "__main__":
    main()

✅ 项目结构推荐:

project/
├── main.py
├── plugin_manager.py
└── plugins/
    ├── __init__.py
    ├── base.py
    ├── echo.py
    └── reverse.py

image.png


📦 支持“无侵入”新增插件

只需在 plugins/ 新增一个 xxx.py,继承 BasePlugin + 注册装饰器即可,无需修改主程序


💡 拓展挑战

  1. 支持插件配置(通过 yaml/json 文件)
  2. 支持插件热重载(结合 watchdog)
  3. 支持命令行参数运行插件(结合 argparse)

🧠 总结一句话

写插件系统不是炫技,而是为未来扩展留出空间:开放-封闭原则的最优实践形式。