如何动态导入模块并将其名称作为字符串?

问题描述 投票:0回答:10

我正在编写一个将命令作为参数的 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)

该函数导入模块

name
,可能使用给定的
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()

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

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


另请参阅: 如何在给定完整路径的情况下动态导入模块?

python python-import
10个回答
408
投票

对于 2.7/3.1 之前的 Python,这几乎就是你的做法。

对于较新版本,请参阅

importlib.import_module
了解 Python 2Python 3

或者使用

__import__
您可以通过执行以下操作导入模块列表:

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

直接摘自深入Python


404
投票

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

importlib
模块:

importlib.import_module(
name, package=None)

导入模块。 name 参数指定以绝对或相对方式导入哪个模块(例如

pkg.mod
..mod
)。如果以相对术语指定名称,则必须将 package 参数设置为包的名称,该包的名称将充当解析包名称的锚点(例如
import_module('..mod', 'pkg.subpkg')
将导入
pkg.mod
)。

例如

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

137
投票

注意:自 Python 3.4 起,imp 已被弃用,取而代之的是 importlib

如前所述,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

50
投票

使用importlib

导入源文件

这是一个稍微修改过的示例来自文档

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)

# Verify contents of the module:
print(dir(module))

从这里开始,

module
将是一个代表
pluginX
模块的模块对象(与通过执行
pluginX
分配给
import pluginX
相同的东西)。因此,调用例如在
hello
中定义的
pluginX
函数(无参数),请使用
module.hello()

要获得“导入”功能

from
模块的效果,请将其存储在已加载模块的内存缓存中,然后执行相应的
from
导入:

sys.modules[module_name] = module

from pluginX import hello
hello()

导入包

要导入包,调用

import_module
就足够了。假设当前工作目录下有一个包文件夹
pluginX
;然后就做吧

import importlib

pkg = importlib.import_module('pluginX')

# check if it's all there..
print(dir(pkg))

18
投票

使用 imp 模块,或更直接的

__import__()
功能。


12
投票

如果您想在当地人中使用:

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


12
投票

您可以使用

exec

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

1
投票

与@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

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

-9
投票

以下对我有用:

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

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

© www.soinside.com 2019 - 2024. All rights reserved.