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

我设想的插件系统:
1、通过类来实现
2、自动查找和导入
我们假设需要实现一个简单的插件系统,插件可以接收一个参数执行。
实现基础插件类
我们先构建一个基础插件类:plugin_collection.py
class Plugin:
"""
该基类每个插件都需要继承,插件需要实现基类定义的方法"""
def __init__(self):
self.description = '未知'
def perform_operation(self, argument):
"""
实际执行插件所执行的方法,该方法所有插件类都需要实现
"""
raise NotImplementedError
所有的插件类需要申明description来进行插件描述,并且要实现perform_operation方法,该方法是实际加载插件将去执行的方法。
简易插件
我们现在实现一个插件,实际执行时仅返回传入的参数: plugins/identity.py
import plugin_collection
class Identity(plugin_collection.Plugin):
"""
This plugin is just the identity function: it returns the argument
"""
def __init__(self):
super().__init__()
self.description = 'Identity function'
def perform_operation(self, argument):
"""
The actual implementation of the identity plugin is to just return the
argument
"""
return argument
动态加载机制
因为我们预实现动态加载插件。我们通过定义一个PluginCollection来完成该职责,它将载入所有的插件,并且根据传入的值执行perform_operation方法。PluginCollection类基础组件实现如下:plugins_collection.py
class PluginCollection:
"""
该类会通过传入的package查找继承了Plugin类的插件类
"""
def __init__(self, plugin_package):
self.plugin_package = plugin_package
self.reload_plugins()
def reload_plugins(self):
"""
重置plugins列表,遍历传入的package查询有效的插件
"""
self.plugins = []
self.seen_paths = []
print()
print(f"在 {self.plugin_package} 包里查找插件")
self.walk_package(self.plugin_package)
def apply_all_plugins_on_value(self, argument):
print()
print(f"执行参数 {argument} 到所有的插件:")
for plugin in self.plugins:
print(f" 执行 {plugin.description} 参数 {argument} 结果 {plugin.perform_operation(argument)}")
最关键的是PluginCollection类里的walk_package方法,该方法按如下步骤操作:
1、操作package里所有的模块
2、针对找到的模块,检查是否是Plugin的子类,非Plugin自身。每个插件将会初始化并加入到列表。该检查的好处是你可以放入其他Python模块,也并不影响插件的使用
3、检查当前package下的子目录,递归查找插件
def walk_package(self, package):
"""
递归遍历包里获取所有的插件
"""
imported_package = __import__(package, fromlist=['blah'])
for _, pluginname, ispkg in pkgutil.iter_modules(imported_package.__path__, imported_package.__name__ + '.'):
if not ispkg:
plugin_module = __import__(pluginname, fromlist=['blah'])
clsmembers = inspect.getmembers(plugin_module, inspect.isclass)
for (_, c) in clsmembers:
# 仅加入Plugin类的子类,忽略掉Plugin本身
if issubclass(c, Plugin) and (c is not Plugin):
print(f' 找到插件类: {c.__module__}.{c.__name__}')
self.plugins.append(c())
# 现在我们已经查找了当前package中的所有模块,现在我们递归查找子packages里的附件模块
all_current_paths = []
if isinstance(imported_package.__path__, str):
all_current_paths.append(imported_package.__path__)
else:
all_current_paths.extend([x for x in imported_package.__path__])
for pkg_path in all_current_paths:
if pkg_path not in self.seen_paths:
self.seen_paths.append(pkg_path)
# 获取当前package中的子目录
child_pkgs = [p for p in os.listdir(pkg_path) if os.path.isdir(os.path.join(pkg_path, p))]
# 递归遍历子目录的package
for child_pkg in child_pkgs:
self.walk_package(package + '.' + child_pkg)
测试
现在我们写个简单的测试:test.py
from plugin_collection import PluginCollection
my_plugins = PluginCollection('plugins')
my_plugins.apply_all_plugins_on_value(5)
执行结果:
$ python3 test.py
在 plugins 包里查找插件
找到插件类: plugins.identity.Identity
执行参数 5 到所有的插件:
执行 Identity function 参数 5 结果 5
代码:https://github.com/erhuabushuo/plugin_template
Python中实现简单的插件框架的更多相关文章
- 用Python写一个简单的Web框架
一.概述 二.从demo_app开始 三.WSGI中的application 四.区分URL 五.重构 1.正则匹配URL 2.DRY 3.抽象出框架 六.参考 一.概述 在Python中,WSGI( ...
- python中一个简单的webserver
python中一个简单的webserver 2013-02-24 15:37:49 分类: Python/Ruby 支持多线程的webserver 1 2 3 4 5 6 7 8 9 10 11 ...
- Python 中lambda 简单介绍
转自:https://www.cnblogs.com/AlwaysWIN/p/6202320.html 在学习python的过程中,lambda的语法经常出现,现在将它整理一下,以备日后查看. 1.l ...
- 正则表达式在python中的简单使用
正则表达式独立与编程语言,基本上所有的编程语言都实现了正则表达式的相关操作.在Python中正则表达式的表现为re模块: import re 其操作有三个方法: my_string = "h ...
- elasticsearch基础及在Python中的简单使用
目录 一. 安装java环境与elasticsearch.kibana 二. elasticsearch.kibana的部分文件说明 三. Kibana的Dev tools中ES的简单命令 四. ES ...
- 在.NET Core中使用简单的插件化机制
前言 插件化,其实也并不是什么新东西了,像nopCommerce等开源项目都有类似的机制,而且功能比较完善和齐全. 相信大家都对接过不少支付方式,支付宝.微信以及各大银行或第三方的支付公司. 我们可以 ...
- python中HTMLParser简单理解
找一个网页,例如https://www.python.org/events/python-events/,用浏览器查看源码并复制,然后尝试解析一下HTML,输出Python官网发布的会议时间.名称和地 ...
- python中最简单的多进程程序
学着.. #!/usr/bin/env python # -*- coding: utf-8 -*- # Spawn a Process: Chapter 3: Process Based Paral ...
- Python中最简单快捷的输出方式
格式化输出最简单的方式之哑巴填充公式 name=ludundun age=25 print(f'hello {name},your age is {age}') 输出内容: hello ludundu ...
随机推荐
- Java面向对象-类与对象
Java面向对象-类与对象 类与对象的关系 我们通俗的举个例子,比如人类是一种类,张三这个人就是人类的具体的一个个体,也就是java中的对象:这就是一个类与对象的关系: 类的定义 下面看实例 类的创建 ...
- Android 使用官方下拉刷新
网上关于下拉刷新的文章也不少,不过都太长了,看得挺难受的.恰好发现了官方的下拉刷新库,而且效果还是不错的,简洁美观,用得也挺方便. 下面是效果图: 我的好友原来是空的,刷新后多了两个. 使用还是挺方便 ...
- JAVA的FileOutput/InputStream使用实例
在JAVA中,要读写文件,要使用Stream这个东西. Stream简单来说,可以看做在程序和文件之间打开了一个管道,然后把数据通过这个管道输送到文件或程序中去. FileOutput/InputSt ...
- 项目引入Solr时应该考虑的一些问题
1.数据更新频率:每天数据增量有多大,随时更新还是定时更新 2.数据总量:数据要保存多长时间 3.一致性要求:期望多长时间内看到更新的数据,最长允许多长时间延迟 4.数据特点:数据源包括哪些,平均单条 ...
- Java,Calendar -- 获取当前日期、当月月初日期、月末日期
public class CalendarTest { public static void main(String[] args) { // 获取当前年份.月份.日期 Calendar cale = ...
- HTML中meta标签的作用与使用
META标签用来描述一个HTML网页文档的属性 META标签可分为两大部分:HTTP-EQUIV和NAME变量. HTTP实例 HTML代码实例中有一项内容是 <meta http-equiv= ...
- SimpleTag——认识自定义标签
自定义标签 自定义标签的开发与应用步骤 编写完成标签功能的 Java 类(标签处理器) 编写标签库描述(tld)文件,在tld文件中对自定义中进行描述 在 JSP 页面中导入和使用自定义标签 ①. 创 ...
- bug记录:IE8,包含块min-height/height共存时的高度计算bug
问题的条件有: A元素是B元素的包含块. A元素设置overflow:hidden;,并同时设置了height和min-height,同时height计算值 < min-height 原生IE8 ...
- 浅谈Android内存管理
最近在网上看了不少Android内存管理方面的博文,但是文章大多都是就单个方面去介绍内存管理,没有能全局把握,缺乏系统性阐述,而且有些观点有误,仅仅知道这些,还是无法从整体上理解内存管理,对培养系统优 ...
- Java 集合框架必记框架图