hhh5460

近日,决定用 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__.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\']
  • 插件是冷插拔的
  • 插件不是懒加载

优化方向

  • 热插拔
  • 懒加载

分类:

技术点:

相关文章:

  • 2021-06-16
  • 2021-06-29
  • 2022-12-23
  • 2021-11-29
  • 2021-10-13
  • 2021-06-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2022-01-10
  • 2021-07-06
  • 2021-09-27
  • 2018-05-21
  • 2022-12-23
  • 2021-05-27
相关资源
相似解决方案