🧙♂️ 真正灵活的系统不是硬编码流程,而是通过插件动态扩展能力。本篇带你用 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
📦 支持“无侵入”新增插件
只需在 plugins/ 新增一个 xxx.py,继承 BasePlugin + 注册装饰器即可,无需修改主程序。
💡 拓展挑战
- 支持插件配置(通过 yaml/json 文件)
- 支持插件热重载(结合 watchdog)
- 支持命令行参数运行插件(结合 argparse)
🧠 总结一句话
写插件系统不是炫技,而是为未来扩展留出空间:开放-封闭原则的最优实践形式。