我正在编写一个python应用程序,我想在其中使用动态的、一次性可运行的插件。
我的意思是,在这个应用程序运行的不同时间,它会在特定位置查找具有特殊名称的python源文件。如果找到任何这样的源文件,我希望我的应用程序加载它,在其中运行一个预先命名的函数(如果存在这样的函数),然后忘记那个源文件。
在应用程序运行的后期,该文件可能已经更改,我希望我的python应用程序重新加载它,执行它的方法,然后像以前一样忘记它。
标准导入系统在初始加载后保持模块驻留,这意味着后续的"import"或"__import__"调用在初始导入后不会重新加载同一模块。因此,在第二次到第n次导入期间,将忽略此源文件中对python代码的任何更改。
为了每次都能唯一地加载这样的包,我想出了以下程序。这很有效,但对我来说有点"粗鲁"。有什么更优雅或更喜欢的方法吗?(注意,以下是一个过于简化的示例)
import sys
import imp
# The following module name can be anything, as long as it doesn't
# change throughout the life of the application ...
modname = '__whatever__'
def myimport(path):
'''Dynamically load python code from "path"'''
# get rid of previous instance, if it exists
try:
del sys.modules[modname]
except:
pass
# load the module
try:
return imp.load_source(modname, path)
except Exception, e:
print 'exception: {}'.format(e)
return None
mymod = myimport('/path/to/plugin.py')
if mymod is not None:
# call the plugin function:
try:
mymod.func()
except:
print 'func() not defined in plugin: {}'.format(path)
补遗:这方面的一个问题是func()在一个单独的模块上下文中运行,它无法访问调用方空间中的任何函数或变量。因此,如果我希望在调用中可以访问func_one()、func_two()和abc的函数():
def func_one():
# whatever
def func_two():
# whatever
abc = '123'
# Load the module as shown above, but before invoking mymod.func(),
# the following has to be done ...
mymod.func_one = func_one
mymod.func_two = func_two
mymod.abc = abc
# This is a PITA, and I'm hoping there's a better way to do all of
# this.
非常感谢。
我使用以下代码来做这类事情。
请注意,我实际上并没有将代码作为模块导入,而是在特定的上下文中执行代码。这让我可以定义一组插件自动可用的api函数,而无需用户导入任何内容。
def load_plugin(filename, context):
source = open(filename).read()
code = compile(source, filename, 'exec')
exec(code, context)
return context['func']
context = { 'func_one': func_one, 'func_two': func_two, 'abc': abc }
func = load_plugin(filename, context)
func()
此方法适用于python2.6+和python3.3+
您使用的方法非常好。对于这个问题,
这样做的一个问题是func()在一个单独的模块上下文中运行,并且它无法访问调用方空间中的任何函数或变量。
最好使用execfile
函数:
# main.py
def func1():
print ('func1 called')
exec(open('trackableClass.py','r').read(),globals()) # this is similar to import except everything is done in the current module
#execfile('/path/to/plugin.py',globals()) # python 2 version
func()
测试:
#/path/to/plugin.py
def func():
func1()
结果:
python main.py
# func1 called
这种方法的一个潜在问题是命名空间污染,因为每个文件都在当前命名空间中运行,这增加了名称冲突的机会。