Python中的dynamic模块导入

我正在编写一个Python应用程序,作为一个参数作为命令,例如:

$ python myapp.py command1 

我希望应用程序是可扩展的,也就是说,能够添加实现新命令的新模块,而不必更改主应用程序源。 树看起来像这样:

 myapp/ __init__.py commands/ __init__.py command1.py command2.py foo.py bar.py 

所以我希望应用程序在运行时find可用的命令模块并执行相应的命令模块。

目前,这是实施像这样的:

 command = sys.argv[1] try: command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"]) except ImportError: # Display error message command_module.run() 

这工作得很好,我只是想知道是否有一个更习惯的方式来完成我们正在做的这个代码。

请注意,我特别不想使用鸡蛋或扩展点。 这不是一个开源项目,我不希望有“插件”。 重点在于简化主应用程序代码,并在每次添加新的命令模块时删除需要修改的代码。

使用比2.7 / 3.1更早的Python,这几乎是你如何做到的。 对于更新的版本,请参阅2.7+和3.1+的 importlib.import_module 。

如果你想要,你也可以使用exec

请注意,您可以通过执行以下操作来导入模块列表:

 >>> moduleNames = ['sys', 'os', 're', 'unittest'] >>> moduleNames ['sys', 'os', 're', 'unittest'] >>> modules = map(__import__, moduleNames) 

从Dive Into Python中直接跳过。

Python 2.7及更高版本的推荐方法是使用importlib模块:

 my_module = importlib.import_module('os.path') 

如上所述, imp模块为您提供了加载function:

 imp.load_source(name, path) imp.load_compiled(name, path) 

我之前使用过这些来执行类似的操作。

在我的情况下,我定义了一个特定的类与定义的方法是必需的。 一旦我加载模块,我会检查类是否在模块中,然后创build该类的实例,如下所示:

 import imp import os def load_from_file(filepath): class_inst = None expected_class = 'MyClass' mod_name,file_ext = os.path.splitext(os.path.split(filepath)[-1]) if file_ext.lower() == '.py': py_mod = imp.load_source(mod_name, filepath) elif file_ext.lower() == '.pyc': py_mod = imp.load_compiled(mod_name, filepath) if hasattr(py_mod, expected_class): class_inst = getattr(py_mod, expected_class)() return class_inst 

使用imp模块 ,或者更直接的__import__()函数。

如果你想在你的当地人:

 >>> mod = 'sys' >>> locals()['my_module'] = __import__(mod) >>> my_module.version '2.6.6 (r266:84297, Aug 24 2010, 18:46:32) [MSC v.1500 32 bit (Intel)]' 

同样可以与globals()

你可以使用exec

 exec "import myapp.commands.%s" % command 

类似于@monkut的解决scheme,但可重用和容错在这里描述http://stamat.wordpress.com/dynamic-module-import-in-python/

 import os import imp def importFromURI(uri, absl): mod = None if not absl: uri = os.path.normpath(os.path.join(os.path.dirname(__file__), uri)) path, fname = os.path.split(uri) mname, ext = os.path.splitext(fname) if os.path.exists(os.path.join(path,mname)+'.pyc'): try: return imp.load_compiled(mname, uri) except: pass if os.path.exists(os.path.join(path,mname)+'.py'): try: return imp.load_source(mname, uri) except: pass return mod 

这听起来像你真正想要的是一个插件架构。

您应该看看setuptools软件包提供的入口点function。 它提供了一个很好的方式来发现为你的应用程序加载的插件。

下面的一块为我工作:

 >>>import imp; >>>fp, pathname, description = imp.find_module("/home/test_module"); >>>test_module = imp.load_module("test_module", fp, pathname, description); >>>print test_module.print_hello(); 

如果你想在shell脚本中导入:

 python -c '<above entire code in one line>' 

以下为我工作:

 import sys, glob sys.path.append('/home/marc/python/importtest/modus') fl = glob.glob('modus/*.py') modulist = [] adapters=[] for i in range(len(fl)): fl[i] = fl[i].split('/')[1] fl[i] = fl[i][0:(len(fl[i])-3)] modulist.append(getattr(__import__(fl[i]),fl[i])) adapters.append(modulist[i]()) 

它从文件夹“modus”加载模块。 模块有一个与模块名称相同的类。 例如,文件modus / modu1.py包含:

 class modu1(): def __init__(self): self.x=1 print self.x 

结果是一个dynamic加载类“适配器”的列表。