如何根据名称导入模块?

Kam*_*iel 489 python python-import

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

$ python myapp.py command1
Run Code Online (Sandbox Code Playgroud)

我希望应用程序是可扩展的,即能够添加实现新命令的新模块,而无需更改主应用程序源.树看起来像:

myapp/
    __init__.py
    commands/
        __init__.py
        command1.py
        command2.py
    foo.py
    bar.py
Run Code Online (Sandbox Code Playgroud)

所以我希望应用程序在运行时找到可用的命令模块并执行适当的命令模块.

Python定义了一个__import__函数,它接受一个模块名称的字符串:

__import __(name,globals = None,locals = None,fromlist =(),level = 0)

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

来源:https://docs.python.org/3/library/functions.html# 进口

所以目前我有类似的东西:

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

command_module.run()
Run Code Online (Sandbox Code Playgroud)

这很好用,我只是想知道是否可能有更惯用的方法来完成我们正在使用此代码.

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

Har*_*mbe 291

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

对于较新的版本,看到importlib.import_modulePython的2和和Python 3中.

你也可以使用exec.

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

>>> moduleNames = ['sys', 'os', 're', 'unittest'] 
>>> moduleNames
['sys', 'os', 're', 'unittest']
>>> modules = map(__import__, moduleNames)
Run Code Online (Sandbox Code Playgroud)

直接从Dive Into Python中扯下来.

  • 正如Denis Malinovsky在下面指出的那样,这个解决方案的另一个问题是python文档本身建议不使用`__import__`.2.7文档:"因为这个函数是供Python解释器使用而不是一般用途所以最好使用importlib.import_module()..."当使用python3时,`imp`模块解决了这个问题,就像monkut提到下面. (6认同)
  • exec的差异是什么? (5认同)
  • OP的这个解决方案的一个问题是捕获一个或两个不良"命令"模块的异常使得他/她的整个命令应用程序在一个例外上失败.我个人想循环遍历每个__​​import__独立包装在一个try:mods = __ import __()\nexcept ImportError as error:report(error)允许其他命令继续工作而坏的修复. (5认同)
  • @JohnWu 为什么当你有 `for element in` 和 `map()` 时还需要 `foreach` :) (2认同)
  • 请注意,地图在 python 3 中不起作用,因为地图现在使用惰性求值。 (2认同)

Den*_*sky 283

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')
Run Code Online (Sandbox Code Playgroud)

  • 文档使用`__import__`函数建议[反对](https://docs.python.org/2/library/functions.html#__import__),以支持上述模块. (61认同)
  • 这适用于导入`os.path`; 如何从os.path导入*`? (7认同)
  • 我在这里得到答案/sf/answers/3114501561/即.调用`globals().update(my_module .__ dict)` (4认同)
  • 由哪个来源或权威机构推荐? (2认同)
  • @NamGVU,这是一种危险的方法,因为它会污染你的全局变量并且可以覆盖任何具有相同名称的标识符.您发布的链接具有此代码的更好,改进版本. (2认同)

mon*_*kut 130

注意:从Python 3.4开始,不推荐使用imp,而是支持importlib

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

imp.load_source(name, path)
imp.load_compiled(name, path)
Run Code Online (Sandbox Code Playgroud)

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

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

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
Run Code Online (Sandbox Code Playgroud)

  • 注意:这个解决方案有效;但是,imp 模块将被弃用,以支持 import lib,请参阅 imp 的页面:“”“自版本 3.4 起已弃用:imp 包正在等待弃用,以支持 importlib。”“” (3认同)
  • 好又简单的解决方案。我写了一个类似的文章:http://stamat.wordpress.com/dynamic-module-import-in-python/但您有一些缺陷:异常情况如何?IOError和ImportError?为什么不先检查编译版本,然后再检查源版本。期望一个类会降低您案例的可重用性。 (2认同)
  • 在目标模块中构造MyClass的行中,您将添加对类名称的冗余引用.它已经存储在`expected_class`中,因此您可以执行`class_inst = getattr(py_mod,expected_name)()`. (2认同)

gim*_*mel 18

使用imp模块或更直接的__import__()功能.

  • 不要使用 \_\_import\_\_ 请参阅 [Python 文档](https://docs.python.org/3/library/functions.html#__import__) 使用 [importlib.import_module](https://docs .python.org/3/library/importlib.html#importlib.import_module) (2认同)

Jon*_*han 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)]'
Run Code Online (Sandbox Code Playgroud)

同样适用 globals()

  • 内置 `locals()` 函数的[文档](https://docs.python.org/3/library/functions.html#locals)明确警告“不应修改此字典的内容” 。 (2认同)

Gre*_*ill 9

你可以使用exec:

exec "import myapp.commands.%s" % command
Run Code Online (Sandbox Code Playgroud)

  • 然后,您可以执行getattr(myapp.commands,command)来访问该模块. (5认同)

Bra*_*ndt 9

Nowadays you should use importlib.

Import a source file

The docs actually provide a recipe for that, and it goes like:

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)
Run Code Online (Sandbox Code Playgroud)

Import a package

Importing a package (e.g., pluginX/__init__.py) under your current dir is actually straightforward:

import importlib

pkg = importlib.import_module('pluginX')

# check if it's all there..
def bla(mod):
    print(dir(mod))
bla(pkg)
Run Code Online (Sandbox Code Playgroud)

  • 如果您希望之后能够“导入 module_name”,请将“sys.modules[module_name] = module”添加到代码的末尾 (4认同)