python做为一个动态语言,可以很方便的调用,在程序需要的时候去调用,而且是动态调用的。这为程序开发带来了很大的方便。很多程序都采用了插件式开发,因为方便扩展。在python里,有什么好方法实现插件了,我实现了一个简单的插件:
1. 定义一个插件目录,所有插件都放在这个目录里面。
2. 定义插件要实现的基类,主要是为了插件管理分类方便,python作为动态语言,基类,接口没有太大的意义,因为随时可以扩展。
3. 定义插件管理器,用插件管理器去load 插件
4. 测试调用插件
整个程序目录结构如下(我用pydev做的):
iPlugin 是所有插件必须实现的接口,而且里面的name 属性必须定义,插件管理器,根据这个name 去调用插件.#coding:utf-8
class Plugin(object):
""" 定义一个接口,其他 插件必须实现这个接口,name 属性必须赋值 """
name = ''
description = ''
version = ''
def __init__(self):
pass
def executeFun(self):
pass
插件管理器的实现
因为可能有很多种方式管理插件,不同的插件管理器,实现方式不一样,所以定义个插件管理器的基类,各种具体的插件管理器是实现这个基类,比如这里实现了一个 基于目录结构去查找插件的 DirectoryPluginManager
#coding:utf-8
from iPlugin import Plugin
from imp import find_module,load_module,acquire_lock,release_lock
import os
import sys
class PluginManager(object):
"""Base class for plugin managers. Does not implement loadPlugins, so it
may only be used with a static list of plugins.
"""
name = "base"
def __init__(self, plugins=(), config={}):
self.__plugins = []
if plugins:
self.addPlugins(plugins)
def __iter__(self):
return iter(self.plugins)
def addPlugin(self, plug):
print 'PluginManager add plugin:',plug
self.__plugins.append(plug)
def addPlugins(self, plugins):
for plug in plugins:
self.addPlugin(plug)
def delPlugin(self, plug):
if plug in self.__plugins:
self.__plugins.remove(plug)
def delPlugins(self, plugins):
for plug in plugins:
self.delPlugin(plug)
def getPlugins(self,name=None):
plugins = []
print 'self.__plugins:',self.plugins
for plugin in self.__plugins:
print 'plugin.name',plugin.name
if (name is None or plugin.name == name):
plugins.append(plugin)
return plugins
def _loadPlugin(self, plug):
loaded = False
print '******PluginManager _loadPlugin ,',self.plugins
for p in self.plugins:
if p.name == plug.name:
loaded = True
break
if not loaded:
self.addPlugin(plug)
print "%s: loaded plugin %s " % (self.name, plug.name)
def loadPlugins(self):
pass
def _get_plugins(self):
return self.__plugins
def _set_plugins(self, plugins):
self.__plugins = []
self.addPlugins(plugins)
plugins = property(_get_plugins, _set_plugins, None,
"""Access the list of plugins managed by
this plugin manager""")
class DirectoryPluginManager(PluginManager):
"""Plugin manager that loads plugins from plugin directories.
"""
name = "directory"
def __init__(self, plugins=(), config={}):
default_directory = os.path.join(os.path.dirname(__file__),"plugins")
self.directories = config.get("directories", (default_directory,))
print '========DirectoryPlugManager========',plugins
PluginManager.__init__(self, plugins, config)
def loadPlugins(self):
"""Load plugins by iterating files in plugin directories.
"""
plugins = []
print '********Directory directories:',self.directories
for dir in self.directories:
try:
for f in os.listdir(dir):
if f.endswith(".py") and f != "__init__.py":
plugins.append((f[:-3], dir))
except OSError:
print "Failed to access: %s" % dir
continue
fh = None
mod = None
print '********Directory all plugins:',plugins
for (name, dir) in plugins:
try:
acquire_lock()
fh, filename, desc = find_module(name, [dir])
print '********Directory fh,filename,desc:',fh,filename,desc,name
old = sys.modules.get(name)
if old is not None:
# make sure we get a fresh copy of anything we are trying
# to load from a new path
del sys.modules[name]
mod = load_module(name, fh, filename, desc)
finally:
if fh:
fh.close()
release_lock()
if hasattr(mod, "__all__"):
print '********Directory mod __all__:',mod.__all__
attrs = [getattr(mod, x) for x in mod.__all__]
print '********Directory attrs:',attrs
for plug in attrs:
if not issubclass(plug, Plugin):
continue
self._loadPlugin(plug())
在plugin 目录下 写自己的插件,注意必须实现Plugin 接口,并且必须写name属性
#coding:utf-8
import sys
sys.path.append('../')
from iPlugin import Plugin
__all__ = ["FirstPlugin"]
class FirstPlugin(Plugin):
name = "firstPlugin"
version = '0.0.1'
def __init__(self):
Plugin.__init__(self)
def scan(self, config={}):
return "first plugin"
def execFun(self):
return "exec function"
调用插件
通过插件管理器去调用插件,用你自己需要的插件管理器去调用,我这里只实现了一个基于目录结构的。
#coding:utf-8
from pluginManager import DirectoryPluginManager
import os
if __name__=='__main__':
plugin_manager = DirectoryPluginManager()
plugin_manager.loadPlugins()
plugins = plugin_manager.getPlugins("firstPlugin")
print "**"*30
print plugins[0].execFun()
你会发现,DirectoryPluginManager 找到了名字为 firstPlugin 插件,并成功执行了调用,值得注意的是,在所有的 实现的插件里面,我都加入了一个 __all__属性 暴露公开的类,同时在 pluginManager 里面也用到这个属性。
源代码下载:
点击下载此文件