近日,决定用 python 实现插件架构,于是上 stackoverflow 逛了一下,在这里发现一段代码,非常喜欢。

提醒各位大侠注意,我对这段代码作了一点小小的改动:原 PLUGINS 是 list 对象,改动后 PLUGINS 是 dict 对象。

代码先贴出来,以飨观众:

  1. ''' 插件架构 '''
  2. # 平台
  3. class TextProcessor(object):
  4. PLUGINS = {}
  5. def process(self, text, plugins=()):
  6. if plugins is ():
  7. for plugin_name in self.PLUGINS.keys():
  8. text = self.PLUGINS[plugin_name]().process(text)
  9. else:
  10. for plugin_name in plugins:
  11. text = self.PLUGINS[plugin_name]().process(text)
  12. return text
  13. @classmethod
  14. def plugin_register(cls, plugin_name):
  15. def wrapper(plugin):
  16. cls.PLUGINS.update({plugin_name:plugin})
  17. return plugin
  18. return wrapper
  19. # 插件
  20. @TextProcessor.plugin_register('plugin1')
  21. class CleanMarkdownBolds(object):
  22. def process(self, text):
  23. return text.replace('**', '')
  24. # 测试
  25. processor = TextProcessor()
  26. print(processor.PLUGINS) # {’plugin1': <class '__main__.CleanMarkdownBolds'>}
  27. processed = processor.process(text="**foo bar**", plugins=('plugin1', ))
  28. processed = processor.process(text="**foo bar**")

这段代码运行良好!但是它是单文件,不适合实际使用。

在实际项目中,上面的三个注释下面的部分一定是拆开的,其中插件一般都约定俗成地放到 plugins 子目录下。

为了实现这个想法,走了很多弯路,花了两天时间!这期间查阅了__metaclass__原理, __subclass__()函数, package的组织方式等等。最后真的灵光一闪,成功实现!

项目结构:

  1. ├─ myproject
  2. ├─ run.py
  3. ├─ app
  4. ├─ __init__.py
  5. ├─ main.py
  6. ├─ platform.py
  7. ├─ plugins
  8. ├─ __init__.py
  9. ├─ plugin1.py
  10. ├─ plugin2.py

完整代码

  1. # mpyproject/app/platform.py
  2. class TextProcessor(object):
  3. PLUGINS = {}
  4. def process(self, text, plugins=()):
  5. if plugins is ():
  6. for plugin_name in self.PLUGINS.keys():
  7. text = self.PLUGINS[plugin_name]().process(text)
  8. else:
  9. for plugin_name in plugins:
  10. text = self.PLUGINS[plugin_name]().process(text)
  11. return text
  12. @classmethod
  13. def plugin_register(cls, plugin_name):
  14. def wrapper(plugin):
  15. cls.PLUGINS.update({plugin_name:plugin})
  16. return plugin
  17. return wrapper
  18. # mpyproject/app/plugins/plugin1.py
  19. from ..platform import TextProcessor
  20. @TextProcessor.plugin_register('plugin1')
  21. class CleanMarkdownBolds(object):
  22. def process(self, text):
  23. return text.replace('**', '')
  24. # mpyproject/app/plugins/plugin2.py
  25. # 第二个插件!
  26. from ..platform import TextProcessor
  27. @TextProcessor.plugin_register('plugin2')
  28. class CleanMarkdownItalic(object):
  29. def process(self, text):
  30. return text.replace('--', '')
  31. # mpyproject/app/main.py
  32. from .platform import TextProcessor
  33. def test():
  34. processor = TextProcessor()
  35. print(processor.PLUGINS) # {’plugin1': <class '__main__.CleanMarkdownBolds'>}
  36. processed = processor.process(text="**foo bar**", plugins=('plugin1', ))
  37. processed = processor.process(text="--foo bar--")
  38. # mpyproject/app/__init__.py
  39. from .plugins import *
  40. # mpyproject/app/plugins/__init__.py
  41. __all__ = ['plugin1', 'plugin2']
  42. # mpyproject/run.py
  43. from app.main import test
  44. test()

说明:

  • 优雅地实现插件架构,app/__init__.pyapp/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. [1] 插件架构(PLUG-IN)

    网上的一种比较好对插件的定义是:插件(Plug-in,又称addin.add-in.addon或add-on,又译外挂)也称为扩展,是一种遵循一定规范的应用程序接口编写出来的程序,主要是用来扩展软件功 ...

  2. 在C#程序中实现插件架构

    阅读提示:这篇文章将讲述如何利用C#奇妙的特性,实现插件架构,用插件(plug-ins)机制建立可扩展的解决方案. 在.NET框架下的C#语言,和其他.NET语言一样提供了很多强大的特性和机制.其中一 ...

  3. 【Chromium中文文档】插件架构

    插件架构 转载请注明出处:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_Architecture/Plugin_A ...

  4. Python:开发Sublime插件,方便PHP开发

    Python:开发Sublime插件,方便PHP开发 背景 最近在学习PHP,开发环境选择了Sublime2,开发过程发现执行PHP程序非常不方便,需要自己在浏览器中输入路径以进行调试,这点不如Dre ...

  5. C++插件架构浅谈与初步实现

    一.插件架构初步介绍 想到写本博客,也没想到更好的名字,目前就先命这个名吧.说到插件架构,或许大部分IT从业者都听过或者某些牛人也自己实现过稳定高效的插件框架.目前有很多软件以及库都是基于插件架构,例 ...

  6. vim python自动补全插件:pydiction

    vim python自动补全插件:pydiction 可以实现下面python代码的自动补全: 1.简单python关键词补全 2.python 函数补全带括号 3.python 模块补全 4.pyt ...

  7. Mac Sublime Text 3 配置Python环境及安装插件

    一.下载安装Sublime Text 3 官网下载地址:http://www.sublimetext.com/3 二.配置Python开发环境 1.点击右下角,选择python 2.添加编译环境pyt ...

  8. DELPHI开发LINUX插件架构的程序

    DELPHI开发LINUX插件架构的程序 DELPHI可以开发LINUX配置型插件架构的程序,并且这一套插件架构,同样适用于MSWINDOWS和MAC. 配置插件: 根据配置,动态加载插件:

  9. 咏南跨平台中间件支持LINUX和WINDOWS插件架构

    咏南跨平台中间件支持LINUX和WINDOWS插件架构

随机推荐

  1. Ubuntu 18.04 Server 设置静态IP

    一.背景 Netplan是Ubuntu 17.10中引入的一种新的命令行网络配置实用程序,用于在Ubuntu系统中轻松管理和配置网络设置.它允许您使用YAML抽象来配置网络接口.它可与NetworkM ...

  2. Oracle的sys和system默认密码

    system默认:manager sys默认:change_on_install 使用SQL Plus登录数据库时,system使用密码manager可直接登录. 但如果是sys用户,密码必须加上as ...

  3. 转:检查c#代码内存泄露工具-CLR Profiler工具使用

    大家都知道.net有一套自己的内存(垃圾)回收机制,除非有一些数据(方法)长期占有内存不随着垃圾回收功能而释放内存,这样就造成了我们经常说的内存泄露.内存持续增长得不到释放等问题导致APS.NET网站 ...

  4. 检索 COM 类工厂中 CLSID 为 {00021A20-0000-0000-C000-000000000046} 的组件时失败,原因是出现以下错误: 80080005

    创建Excel对象失败: Excel.Application xApp = new Excel.Application(); 错误提示:{"检索 COM 类工厂中 CLSID 为 {0002 ...

  5. SSH批量分发管理

    ssh服务认证类型主要有两个: 基于口令的安全验证: 基于口令的安全验证的方式就是大家一直在用的,只要知道服务器的ssh连接账户.口令.IP及开发的端口,默认22,就可以通过ssh客户端登陆到这台远程 ...

  6. python-异常处理try_except

    异常处理try-except 在我们写程序的时候经常会遇到一些异常或错误,导致程序终止 当我们使用计算器时,用10除以0会提示 一个简单的错误代码(10/0) a = 10 / 0 print(&qu ...

  7. MySQL报错:error1130

    ERROR (HY000): Host 'ip-172-31-x-x.ec2.internal' is not allowed to connect to this MySQL server 分析,从 ...

  8. Linux之因BASH造成的键盘错误和环境问题

    对于Linux我们习惯使用/bin/bash.并且大多数人操作在Centos系统上,但是仍有不少人在ubuntu上使用,两个操作系统大同小异.都是使用了Linux内核.接下来就来讲讲我使用过程中两个系 ...

  9. 深入浅出SharePoint——Search疑难排除

    通过Search log http://richardstk.com/2013/12/23/using-the-sharepoint-2013-search-query-tool-with-searc ...

  10. Java中Map根据键值(key)或者值(value)进行排序实现

    我们都知道,java中的Map结构是key->value键值对存储的,而且根据Map的特性,同一个Map中 不存在两个Key相同的元素,而value不存在这个限制.换句话说,在同一个Map中Ke ...