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

$ python myapp.py command1

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

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

因此,我希望应用程序在运行时找到可用的命令模块并执行适当的命令模块。

Python定义了一个__import__()函数,它接受一个字符串作为模块名:

__import__(name, globals=None, locals=None, fromlist=(), level=0) 该函数导入模块名,可能会使用给定的全局变量和局部变量来确定如何在包上下文中解释该名称。fromlist给出了应该从模块中导入的对象或子模块的名称。 来源:https://docs.python.org/3/library/functions.html __import__

所以目前我有这样的东西:

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,基本上就是这样做的。

有关更新版本,请参见importlib。import_module用于Python 2和Python 3。

或者使用__import__你可以导入一个模块列表,这样做:

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

直接摘自《潜入Python》。

其他回答

对于Python 2.7和3.1及更高版本,建议使用importlib模块:

importlib。import_module(名称、包=没有) 导入模块。name参数以绝对或相对形式指定要导入的模块(例如pkg.mod或..mod)。如果以相对形式指定名称,则package参数必须设置为包的名称,该名称将作为解析包名称的锚点(例如import_module('..Mod ', 'pkg.subpkg')将导入pkg.mod)。

e.g.

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

你可以使用exec:

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

对于2.7/3.1以上的Python,基本上就是这样做的。

有关更新版本,请参见importlib。import_module用于Python 2和Python 3。

或者使用__import__你可以导入一个模块列表,这样做:

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

直接摘自《潜入Python》。

如果你想在局部函数中使用它:

>>> 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()

下面的方法对我很有效:

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

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