python所有插件框架_Python中实现简单的插件框架
在系統(tǒng)設(shè)計(jì)中,經(jīng)常我們希望設(shè)計(jì)一套插件機(jī)制,在不修改程序主體情況下,動(dòng)態(tài)去加載附能。
我設(shè)想的插件系統(tǒng):
1、通過類來實(shí)現(xiàn)
2、自動(dòng)查找和導(dǎo)入
我們假設(shè)需要實(shí)現(xiàn)一個(gè)簡單的插件系統(tǒng),插件可以接收一個(gè)參數(shù)執(zhí)行。
實(shí)現(xiàn)基礎(chǔ)插件類
我們先構(gòu)建一個(gè)基礎(chǔ)插件類:plugin_collection.py
class Plugin:
"""
該基類每個(gè)插件都需要繼承,插件需要實(shí)現(xiàn)基類定義的方法"""
def __init__(self):
self.description = '未知'
def perform_operation(self, argument):
"""
實(shí)際執(zhí)行插件所執(zhí)行的方法,該方法所有插件類都需要實(shí)現(xiàn)
"""
raise NotImplementedError
所有的插件類需要申明description來進(jìn)行插件描述,并且要實(shí)現(xiàn)perform_operation方法,該方法是實(shí)際加載插件將去執(zhí)行的方法。
簡易插件
我們現(xiàn)在實(shí)現(xiàn)一個(gè)插件,實(shí)際執(zhí)行時(shí)僅返回傳入的參數(shù): 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
動(dòng)態(tài)加載機(jī)制
因?yàn)槲覀冾A(yù)實(shí)現(xiàn)動(dòng)態(tài)加載插件。我們通過定義一個(gè)PluginCollection來完成該職責(zé),它將載入所有的插件,并且根據(jù)傳入的值執(zhí)行perform_operation方法。PluginCollection類基礎(chǔ)組件實(shí)現(xiàn)如下:plugins_collection.py
class PluginCollection:
"""
該類會(huì)通過傳入的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"執(zhí)行參數(shù) {argument} 到所有的插件:")
for plugin in self.plugins:
print(f" 執(zhí)行 {plugin.description} 參數(shù) {argument} 結(jié)果 {plugin.perform_operation(argument)}")
最關(guān)鍵的是PluginCollection類里的walk_package方法,該方法按如下步驟操作:
1、操作package里所有的模塊
2、針對找到的模塊,檢查是否是Plugin的子類,非Plugin自身。每個(gè)插件將會(huì)初始化并加入到列表。該檢查的好處是你可以放入其他Python模塊,也并不影響插件的使用
3、檢查當(dāng)前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())
# 現(xiàn)在我們已經(jīng)查找了當(dāng)前package中的所有模塊,現(xiàn)在我們遞歸查找子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)
# 獲取當(dāng)前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)
測試
現(xiàn)在我們寫個(gè)簡單的測試:test.py
from plugin_collection import PluginCollection
my_plugins = PluginCollection('plugins')
my_plugins.apply_all_plugins_on_value(5)
執(zhí)行結(jié)果:
$ python3 test.py
在 plugins 包里查找插件
找到插件類: plugins.identity.Identity
執(zhí)行參數(shù) 5 到所有的插件:
執(zhí)行 Identity function 參數(shù) 5 結(jié)果 5
總結(jié)
以上是生活随笔為你收集整理的python所有插件框架_Python中实现简单的插件框架的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wxpython 日志显示框_wxpyt
- 下一篇: 双绞线传输距离多长_网线数据传输最大距离