如何根据名称导入模块?

时间:2008-11-19 06:09:57

标签: python python-import

我正在编写一个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)

     

该函数导入模块名称,可能使用给定的globals和locals来确定如何解释包上下文中的名称。 fromlist给出了应该从name给出的模块导入的对象或子模块的名称。

     

来源: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()

这很好用,我只是想知道是否有更多惯用的方法来完成我们对这段代码的处理。

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

12 个答案:

答案 0 :(得分:270)

Python早于2.7 / 3.1,这就是你如何做到的。

对于较新版本,请参阅importlib.import_module了解Python 2Python 3

如果您愿意,也可以使用exec

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

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

直接从Dive Into Python撕开。

答案 1 :(得分:261)

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

  

importlib.import_module(name,package = None)

     

导入模块。 name参数指定以绝对或相对术语导入的模块(例如pkg.mod或..mod)。如果名称是以相对术语指定的,那么package参数必须设置为包的名称,该包用作解析包名的锚(例如import_module('.. mod','pkg.subpkg')将导入pkg.mod)。

e.g。

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

答案 2 :(得分:126)

  

注意:自从Python 3.4支持 importlib

以来, imp 已被弃用

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

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

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

在我的例子中,我使用所需的已定义方法定义了一个特定的类。 一旦我加载了模块,我会检查该类是否在模块中,然后创建该类的实例,如下所示:

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

答案 3 :(得分:19)

使用imp module或更直接的__import__()功能。

答案 4 :(得分:13)

如果你想在当地人那里:

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

答案 5 :(得分:10)

您可以使用exec

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

答案 6 :(得分:4)

现在您应该使用importlib

导入源文件

docs实际上为此提供了一个配方,就像这样:

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)实际上很简单:

import importlib

pluginX = importlib.import_module('pluginX')

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

答案 7 :(得分:3)

与@monkut的解决方案类似,但此处描述的可重用和容错: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

答案 8 :(得分:1)

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

您应该查看setuptools包提供的entry points功能。它提供了一种很好的方法来发现为您的应用程序加载的插件。

答案 9 :(得分:1)

例如:我的模块名称类似于jan_module / feb_module / mar_module

  

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

答案 10 :(得分:0)

下面的内容对我有用:

>>>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>'

答案 11 :(得分:-6)

以下对我有用:

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

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