python 优雅地实现插件架构
近日,决定用 python 实现插件架构,于是上 stackoverflow 逛了一下,在这里发现一段代码,非常喜欢。
提醒各位大侠注意,我对这段代码作了一点小小的改动:原 PLUGINS
是 list 对象,改动后 PLUGINS
是 dict 对象。
代码先贴出来,以飨观众:
''' 插件架构 '''
# 平台
class TextProcessor(object):
PLUGINS = {}
def process(self, text, plugins=()):
if plugins is ():
for plugin_name in self.PLUGINS.keys():
text = self.PLUGINS[plugin_name]().process(text)
else:
for plugin_name in plugins:
text = self.PLUGINS[plugin_name]().process(text)
return text
@classmethod
def plugin_register(cls, plugin_name):
def wrapper(plugin):
cls.PLUGINS.update({plugin_name:plugin})
return plugin
return wrapper
# 插件
@TextProcessor.plugin_register('plugin1')
class CleanMarkdownBolds(object):
def process(self, text):
return text.replace('**', '')
# 测试
processor = TextProcessor()
print(processor.PLUGINS) # {’plugin1': <class '__main__.CleanMarkdownBolds'>}
processed = processor.process(text="**foo bar**", plugins=('plugin1', ))
processed = processor.process(text="**foo bar**")
这段代码运行良好!但是它是单文件,不适合实际使用。
在实际项目中,上面的三个注释下面的部分一定是拆开的,其中插件一般都约定俗成地放到 plugins 子目录下。
为了实现这个想法,走了很多弯路,花了两天时间!这期间查阅了__metaclass__
原理, __subclass__()
函数, package的组织方式等等。最后真的灵光一闪,成功实现!
项目结构:
├─ myproject
├─ run.py
├─ app
├─ __init__.py
├─ main.py
├─ platform.py
├─ plugins
├─ __init__.py
├─ plugin1.py
├─ plugin2.py
完整代码
# mpyproject/app/platform.py
class TextProcessor(object):
PLUGINS = {}
def process(self, text, plugins=()):
if plugins is ():
for plugin_name in self.PLUGINS.keys():
text = self.PLUGINS[plugin_name]().process(text)
else:
for plugin_name in plugins:
text = self.PLUGINS[plugin_name]().process(text)
return text
@classmethod
def plugin_register(cls, plugin_name):
def wrapper(plugin):
cls.PLUGINS.update({plugin_name:plugin})
return plugin
return wrapper
# mpyproject/app/plugins/plugin1.py
from ..platform import TextProcessor
@TextProcessor.plugin_register('plugin1')
class CleanMarkdownBolds(object):
def process(self, text):
return text.replace('**', '')
# mpyproject/app/plugins/plugin2.py
# 第二个插件!
from ..platform import TextProcessor
@TextProcessor.plugin_register('plugin2')
class CleanMarkdownItalic(object):
def process(self, text):
return text.replace('--', '')
# mpyproject/app/main.py
from .platform import TextProcessor
def test():
processor = TextProcessor()
print(processor.PLUGINS) # {’plugin1': <class '__main__.CleanMarkdownBolds'>}
processed = processor.process(text="**foo bar**", plugins=('plugin1', ))
processed = processor.process(text="--foo bar--")
# mpyproject/app/__init__.py
from .plugins import *
# mpyproject/app/plugins/__init__.py
__all__ = ['plugin1', 'plugin2']
# mpyproject/run.py
from app.main import test
test()
说明:
- 优雅地实现插件架构,
app/__init__.py
与app/plugins/__init__.py
两个文件起了相互呼应的作用 - 在 app 目录下,除了
app/__init__.py
,不需要在别的任何地方显式地导入插件:from .plugins import *
或from .plugins import plugin1
- 若想添加插件 plugin3.py,可将其复制到 plugins 目录下,然后修改
app/plugins/__init__.py
文件为__all__ = ['plugin1', 'plugin2', 'plugin3']
- 插件是冷插拔的
- 插件不是懒加载
优化方向
- 热插拔
- 懒加载
python 优雅地实现插件架构的更多相关文章
- [1] 插件架构(PLUG-IN)
网上的一种比较好对插件的定义是:插件(Plug-in,又称addin.add-in.addon或add-on,又译外挂)也称为扩展,是一种遵循一定规范的应用程序接口编写出来的程序,主要是用来扩展软件功 ...
- 在C#程序中实现插件架构
阅读提示:这篇文章将讲述如何利用C#奇妙的特性,实现插件架构,用插件(plug-ins)机制建立可扩展的解决方案. 在.NET框架下的C#语言,和其他.NET语言一样提供了很多强大的特性和机制.其中一 ...
- 【Chromium中文文档】插件架构
插件架构 转载请注明出处:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_Architecture/Plugin_A ...
- Python:开发Sublime插件,方便PHP开发
Python:开发Sublime插件,方便PHP开发 背景 最近在学习PHP,开发环境选择了Sublime2,开发过程发现执行PHP程序非常不方便,需要自己在浏览器中输入路径以进行调试,这点不如Dre ...
- C++插件架构浅谈与初步实现
一.插件架构初步介绍 想到写本博客,也没想到更好的名字,目前就先命这个名吧.说到插件架构,或许大部分IT从业者都听过或者某些牛人也自己实现过稳定高效的插件框架.目前有很多软件以及库都是基于插件架构,例 ...
- vim python自动补全插件:pydiction
vim python自动补全插件:pydiction 可以实现下面python代码的自动补全: 1.简单python关键词补全 2.python 函数补全带括号 3.python 模块补全 4.pyt ...
- Mac Sublime Text 3 配置Python环境及安装插件
一.下载安装Sublime Text 3 官网下载地址:http://www.sublimetext.com/3 二.配置Python开发环境 1.点击右下角,选择python 2.添加编译环境pyt ...
- DELPHI开发LINUX插件架构的程序
DELPHI开发LINUX插件架构的程序 DELPHI可以开发LINUX配置型插件架构的程序,并且这一套插件架构,同样适用于MSWINDOWS和MAC. 配置插件: 根据配置,动态加载插件:
- 咏南跨平台中间件支持LINUX和WINDOWS插件架构
咏南跨平台中间件支持LINUX和WINDOWS插件架构
随机推荐
- Ubuntu 18.04 Server 设置静态IP
一.背景 Netplan是Ubuntu 17.10中引入的一种新的命令行网络配置实用程序,用于在Ubuntu系统中轻松管理和配置网络设置.它允许您使用YAML抽象来配置网络接口.它可与NetworkM ...
- Oracle的sys和system默认密码
system默认:manager sys默认:change_on_install 使用SQL Plus登录数据库时,system使用密码manager可直接登录. 但如果是sys用户,密码必须加上as ...
- 转:检查c#代码内存泄露工具-CLR Profiler工具使用
大家都知道.net有一套自己的内存(垃圾)回收机制,除非有一些数据(方法)长期占有内存不随着垃圾回收功能而释放内存,这样就造成了我们经常说的内存泄露.内存持续增长得不到释放等问题导致APS.NET网站 ...
- 检索 COM 类工厂中 CLSID 为 {00021A20-0000-0000-C000-000000000046} 的组件时失败,原因是出现以下错误: 80080005
创建Excel对象失败: Excel.Application xApp = new Excel.Application(); 错误提示:{"检索 COM 类工厂中 CLSID 为 {0002 ...
- SSH批量分发管理
ssh服务认证类型主要有两个: 基于口令的安全验证: 基于口令的安全验证的方式就是大家一直在用的,只要知道服务器的ssh连接账户.口令.IP及开发的端口,默认22,就可以通过ssh客户端登陆到这台远程 ...
- python-异常处理try_except
异常处理try-except 在我们写程序的时候经常会遇到一些异常或错误,导致程序终止 当我们使用计算器时,用10除以0会提示 一个简单的错误代码(10/0) a = 10 / 0 print(&qu ...
- MySQL报错:error1130
ERROR (HY000): Host 'ip-172-31-x-x.ec2.internal' is not allowed to connect to this MySQL server 分析,从 ...
- Linux之因BASH造成的键盘错误和环境问题
对于Linux我们习惯使用/bin/bash.并且大多数人操作在Centos系统上,但是仍有不少人在ubuntu上使用,两个操作系统大同小异.都是使用了Linux内核.接下来就来讲讲我使用过程中两个系 ...
- 深入浅出SharePoint——Search疑难排除
通过Search log http://richardstk.com/2013/12/23/using-the-sharepoint-2013-search-query-tool-with-searc ...
- Java中Map根据键值(key)或者值(value)进行排序实现
我们都知道,java中的Map结构是key->value键值对存储的,而且根据Map的特性,同一个Map中 不存在两个Key相同的元素,而value不存在这个限制.换句话说,在同一个Map中Ke ...