zoukankan      html  css  js  c++  java
  • 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__.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']
    • 插件是冷插拔的
    • 插件不是懒加载

    优化方向

    • 热插拔
    • 懒加载
  • 相关阅读:
    金融学习--外汇储备信息
    odoo开发笔记 -- 跨域Refused to display in a frame because it set 'X-Frame-Options' to 'DENY'
    odoo开发笔记--ValueError Expected singleton
    chartjs
    在线海报设计
    odoo开发笔记--日期or时间字段给定默认值
    odoo开发笔记--form视图按钮样例
    python离线包下载地址
    odoo开发笔记--定时任务源码分析
    运维笔记--linux下忘记mysql root密码
  • 原文地址:https://www.cnblogs.com/hhh5460/p/6681363.html
Copyright © 2011-2022 走看看