在系统设计中,经常我们希望设计一套插件机制,在不修改程序主体情况下,动态去加载附能。

我设想的插件系统:

1、通过类来实现

2、自动查找和导入

我们假设需要实现一个简单的插件系统,插件可以接收一个参数执行。

实现基础插件类

我们先构建一个基础插件类:plugin_collection.py

  1. class Plugin:
  2. """
  3. 该基类每个插件都需要继承,插件需要实现基类定义的方法"""
  4. def __init__(self):
  5. self.description = '未知'
  6. def perform_operation(self, argument):
  7. """
  8. 实际执行插件所执行的方法,该方法所有插件类都需要实现
  9. """
  10. raise NotImplementedError

所有的插件类需要申明description来进行插件描述,并且要实现perform_operation方法,该方法是实际加载插件将去执行的方法。

简易插件

我们现在实现一个插件,实际执行时仅返回传入的参数: plugins/identity.py

  1. import plugin_collection
  2. class Identity(plugin_collection.Plugin):
  3. """
  4. This plugin is just the identity function: it returns the argument
  5. """
  6. def __init__(self):
  7. super().__init__()
  8. self.description = 'Identity function'
  9. def perform_operation(self, argument):
  10. """
  11. The actual implementation of the identity plugin is to just return the
  12. argument
  13. """
  14. return argument

动态加载机制

因为我们预实现动态加载插件。我们通过定义一个PluginCollection来完成该职责,它将载入所有的插件,并且根据传入的值执行perform_operation方法。PluginCollection类基础组件实现如下:plugins_collection.py

  1. class PluginCollection:
  2. """
  3. 该类会通过传入的package查找继承了Plugin类的插件类
  4. """
  5. def __init__(self, plugin_package):
  6. self.plugin_package = plugin_package
  7. self.reload_plugins()
  8. def reload_plugins(self):
  9. """
  10. 重置plugins列表,遍历传入的package查询有效的插件
  11. """
  12. self.plugins = []
  13. self.seen_paths = []
  14. print()
  15. print(f"在 {self.plugin_package} 包里查找插件")
  16. self.walk_package(self.plugin_package)
  17. def apply_all_plugins_on_value(self, argument):
  18. print()
  19. print(f"执行参数 {argument} 到所有的插件:")
  20. for plugin in self.plugins:
  21. print(f" 执行 {plugin.description} 参数 {argument} 结果 {plugin.perform_operation(argument)}")

最关键的是PluginCollection类里的walk_package方法,该方法按如下步骤操作:

1、操作package里所有的模块

2、针对找到的模块,检查是否是Plugin的子类,非Plugin自身。每个插件将会初始化并加入到列表。该检查的好处是你可以放入其他Python模块,也并不影响插件的使用

3、检查当前package下的子目录,递归查找插件

  1. def walk_package(self, package):
  2. """
  3. 递归遍历包里获取所有的插件
  4. """
  5. imported_package = __import__(package, fromlist=['blah'])
  6. for _, pluginname, ispkg in pkgutil.iter_modules(imported_package.__path__, imported_package.__name__ + '.'):
  7. if not ispkg:
  8. plugin_module = __import__(pluginname, fromlist=['blah'])
  9. clsmembers = inspect.getmembers(plugin_module, inspect.isclass)
  10. for (_, c) in clsmembers:
  11. # 仅加入Plugin类的子类,忽略掉Plugin本身
  12. if issubclass(c, Plugin) and (c is not Plugin):
  13. print(f' 找到插件类: {c.__module__}.{c.__name__}')
  14. self.plugins.append(c())
  15. # 现在我们已经查找了当前package中的所有模块,现在我们递归查找子packages里的附件模块
  16. all_current_paths = []
  17. if isinstance(imported_package.__path__, str):
  18. all_current_paths.append(imported_package.__path__)
  19. else:
  20. all_current_paths.extend([x for x in imported_package.__path__])
  21. for pkg_path in all_current_paths:
  22. if pkg_path not in self.seen_paths:
  23. self.seen_paths.append(pkg_path)
  24. # 获取当前package中的子目录
  25. child_pkgs = [p for p in os.listdir(pkg_path) if os.path.isdir(os.path.join(pkg_path, p))]
  26. # 递归遍历子目录的package
  27. for child_pkg in child_pkgs:
  28. self.walk_package(package + '.' + child_pkg)

测试

现在我们写个简单的测试:test.py

  1. from plugin_collection import PluginCollection
  2. my_plugins = PluginCollection('plugins')
  3. my_plugins.apply_all_plugins_on_value(5)

执行结果:

  1. $ python3 test.py
  2. plugins 包里查找插件
  3. 找到插件类: plugins.identity.Identity
  4. 执行参数 5 到所有的插件:
  5. 执行 Identity function 参数 5 结果 5

代码:https://github.com/erhuabushuo/plugin_template

Python中实现简单的插件框架的更多相关文章

  1. 用Python写一个简单的Web框架

    一.概述 二.从demo_app开始 三.WSGI中的application 四.区分URL 五.重构 1.正则匹配URL 2.DRY 3.抽象出框架 六.参考 一.概述 在Python中,WSGI( ...

  2. python中一个简单的webserver

     python中一个简单的webserver 2013-02-24 15:37:49 分类: Python/Ruby 支持多线程的webserver   1 2 3 4 5 6 7 8 9 10 11 ...

  3. Python 中lambda 简单介绍

    转自:https://www.cnblogs.com/AlwaysWIN/p/6202320.html 在学习python的过程中,lambda的语法经常出现,现在将它整理一下,以备日后查看. 1.l ...

  4. 正则表达式在python中的简单使用

    正则表达式独立与编程语言,基本上所有的编程语言都实现了正则表达式的相关操作.在Python中正则表达式的表现为re模块: import re 其操作有三个方法: my_string = "h ...

  5. elasticsearch基础及在Python中的简单使用

    目录 一. 安装java环境与elasticsearch.kibana 二. elasticsearch.kibana的部分文件说明 三. Kibana的Dev tools中ES的简单命令 四. ES ...

  6. 在.NET Core中使用简单的插件化机制

    前言 插件化,其实也并不是什么新东西了,像nopCommerce等开源项目都有类似的机制,而且功能比较完善和齐全. 相信大家都对接过不少支付方式,支付宝.微信以及各大银行或第三方的支付公司. 我们可以 ...

  7. python中HTMLParser简单理解

    找一个网页,例如https://www.python.org/events/python-events/,用浏览器查看源码并复制,然后尝试解析一下HTML,输出Python官网发布的会议时间.名称和地 ...

  8. python中最简单的多进程程序

    学着.. #!/usr/bin/env python # -*- coding: utf-8 -*- # Spawn a Process: Chapter 3: Process Based Paral ...

  9. Python中最简单快捷的输出方式

    格式化输出最简单的方式之哑巴填充公式 name=ludundun age=25 print(f'hello {name},your age is {age}') 输出内容: hello ludundu ...

随机推荐

  1. Python 约束 , 自定义异常 , 加密 , 日志

    约束 约束 , 约束其派生类:  保证派生类中必须编写send方法 , 不然执行可能就会报错 Python中  语法: class BaseMessage(object): def send(self ...

  2. Android 学习 - Telnet 控制AVD

    启动Android Virtual Device之后,使用telnet,可以方便地控制AVD. 首先,获取端口号.启动AVD后,在标题栏上方会出现端口号:设备名,在下面的截图中为5554:N4-15- ...

  3. Visual Studio 2005 C# 读写Excel文件

    做作业的时候查了一点儿资料, 用的vs2k5 读 excel 发现用起来非常简单...现在编程语言没话说! 项目-添加引用-COM-Microsoft Excel 12.0 Object Librar ...

  4. 【Dubbo学习】

    dubbo的介绍 dubbo是一个分布式的开源框架,其核心部分如下: 1.服务提供者:provider 2.服务消费者:consumer 3.注册中心:registry (仅仅只是负责通知) 服务者在 ...

  5. 修改eclipse默认workspace

    三种方法 (只改其一可能无效,最好都试试) 1. 修改exlipse安装目录下\configuration\.settings\org.eclipse.ui.ide.prefs文件,修改RECENT_ ...

  6. AngularJS学习(一)

    参考文章:http://blog.csdn.net/dc_726/article/details/17010325 1.HelloWorld篇 1.1 环境 下载:angular-1.2.5min.j ...

  7. 斐波那契数列,跳台阶(dp思想)

    一 . 斐波那契数列:1,1,2,3,5,8,13,21 即后一项是前两项的和. class Solution { private: ]; public: Solution() { memset(ar ...

  8. laravel框架容器管理的一些要点(转)

    本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. ...

  9. Spring总结八:jdbcTemplate的简单使用

    简介: Srping毕竟是一站式框架,所以也有操作数据库的东西,那就是jdbcTemplate,介绍一下jdbcTemplate的简单使用. 除了要引入必要的包以外,还需要引入 spring-jdbc ...

  10. .net中动态对象的使用

    js中的写法: var list = []; var o = {}; o.id = '111'; o.name = '222'; list.push(o); c#中的写法: var aList = n ...