python:如何导入一个给定名称的模块?

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

1
$ python myapp.py command1

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

1
2
3
4
5
6
7
8
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)

The function imports the module name, potentially using the given globals and locals to determine how to interpret the name in a package context. The fromlist gives the names of objects or submodules that should be imported from the module given by name.

Source: https://docs.python.org/3/library/functions.html#import

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

1
2
3
4
5
6
7
command = sys.argv[1]
try:
    command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"])
except ImportError:
    # Display error message

command_module.run()

这工作得很好,我只是想知道是否有一种更习惯的方法来完成我们用这段代码所做的事情。

注意,我特别不想使用egg或扩展点。这不是一个开源项目,我也不期望有"插件"。重点是简化主应用程序代码,并消除每次添加新命令模块时修改它的需要。


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

对于较新的版本,Python 2和Python 3请参见importlib.import_module

如果需要,也可以使用exec

或者使用__import__,您可以通过以下操作导入模块列表:

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

直接从一头扎进巨蟒。


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

importlib.import_module(name, package=None)

Import a module. The name argument specifies what module to import in absolute or relative terms (e.g. either pkg.mod or ..mod). If the name is specified in relative terms, then the package argument must be set to the name of the package which is to act as the anchor for resolving the package name (e.g. import_module('..mod', 'pkg.subpkg') will import pkg.mod).

如。

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


Note: imp is deprecated since Python 3.4 in favor of importlib

如前所述,imp模块提供了加载功能:

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

我以前也用过类似的方法。

在我的例子中,我定义了一个特定的类,并定义了所需的方法。一旦我加载模块,我将检查类是否在模块中,然后创建一个类的实例,像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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__()函数。


如果你想让你的当地人也这样:

1
2
3
4
>>> 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:

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

现在应该使用importlib。

导入源文件

文档实际上提供了一个食谱,它是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sys
import importlib.util

file_path = 'pluginX.py'
module_name = 'pluginX'

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

# check if it's all there..
def bla(mod):
    print(dir(mod))
bla(module)

导入包

在当前目录下导入包(例如pluginX/__init__.py)实际上很简单:

1
2
3
4
5
6
7
8
import importlib

pluginX = importlib.import_module('pluginX')

# check if it's all there..
def bla(mod):
    print(dir(mod))
bla(module)

例如:我的模块名是jan_module/ feb_module/ mar_module

month='feb'
exec 'from %s_module import *'%(month)


听起来你真正想要的是插件架构。

您应该了解setuptools包提供的入口点功能。它提供了一个发现为您的应用程序加载的插件的好方法。


下面这篇文章对我很有用:

1
2
3
4
>>>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脚本:

1
python -c ''


下面这些对我很有用:

1
2
3
4
5
6
7
8
9
10
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/ module .py文件包含:

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

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