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 ...
随机推荐
- delphi 手电筒
Self.CameraComponent1.TorchMode := TTorchMode.ModeOn;
- 关于jquery在页面初始化时radio控件选定默认值的问题
网上找了很多资料,都是比较旧版本的方法,新版的jquery都已经抛弃了. 正确的代码是 $('input:radio[name="statusRadios"][value=&quo ...
- Android Architecture Components
https://developer.android.com/topic/libraries/architecture/index.html ViewModel 有LiveData Activity 监 ...
- 【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 5 Octave Tutorial—5.6 向量化 Vectorization
5.6 向量化 Vectorization 参考视频: 5 - 6 - Vectorization (14 min).mkv 下面是向量化的小例子,如果将所有u(j) .所有v(j).所有w(j)都看 ...
- 747. Largest Number At Least Twice of Others比所有数字都大两倍的最大数
[抄题]: In a given integer array nums, there is always exactly one largest element. Find whether the l ...
- 1-new对象与直接构建对象
#include <iostream> using namespace std; class A { public: A(){} A (int a){ this->a = a; } ...
- PHP加密与解密
password_hash ( string $password , integer $algo [, array $options ] ) 加密,生成60位得字符串 $algo:一个用来在散列密码时 ...
- js 禁止后退键
function doKey(e) { var ev = e || window.event; //获取event对象 var obj = ev.target || ev.srcElement; // ...
- 获取表中唯一字符串uuid,可用于随机文件名
在mysql数据库中,可以使用uuid()语句来生成一个UUID:例如:mysql> select uuid();+--------------------------------------+ ...
- 使用Recyclerview实现图片水平自动循环滚动
简介: 本篇博客主要介绍的是如何使用RecyclerView实现图片水平方向自动循环(跑马灯效果) 效果图: 思路: 1.准备m张图片 1.使用Recyclerview实现,返回无数个(实际Inter ...