在 python3 中使用 importlib 进行动态导入时出错

Errors while dynamic imports using importlib in python3

我一直在尝试将 importlib 与 python3 (3.6) 一起使用。

目录结构

main.py

#Note: I will only modify line 4 that uses importlib
import importlib
if __name__ == '__main__':
    print("In main.py")
    hello = importlib.import_module('hello', package='./')
    print("Loaded hello.py")
    hello.hello()

hello.py

def hello():
    print('Hello world')

folder/hello.py

def hello():
    print('Hello world in folder')

观察结果

如果我这样做

hello = importlib.import_module('hello', package='./')

hello = importlib.import_module('hello')

它从根文件夹导入hello.py并打印hello world

如果我这样做

hello = importlib.import_module('folder.hello')

它从根文件夹导入 folder/hello.py 并打印 hello world in folder.

但如果我这样做

hello = importlib.import_module('hello', package='folder')

hello = importlib.import_module('hello', package='./folder')

报错

Traceback (most recent call last):
  File "main.py", line 4, in <module>
    hello = importlib.import_module('hello', package='./folder')
  File "/usr/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 953, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'hello'

问题

我不确定这里发生了什么。我很确定我对 python 模块和包的理解有问题。有人可以解释为什么这是预期的行为吗?

如果第一个参数,要导入的模块是绝对模块引用(没有前导.),第二个参数将被完全忽略。

要相对于另一个模块 b 导入模块 a,您必须使用

a = importlib.import_module('.a', package='b')

在你的情况下,这应该有效

hello = importlib.import_module('.hello', package='folder')

根据经验,如果您想使用 package 作为第二个参数,import package 应该有效。

from package import module

然后变成

importlib.import_module(module, package)

@Mahesh 的回答 100% 正确且准确,但我想我们需要更深入地了解它

下面是 import_module

的代码
def import_module(name, package=None):
    """Import a module.

    The 'package' argument is required when performing a relative import. It
    specifies the package to use as the anchor point from which to resolve the
    relative import to an absolute import.

    """
    level = 0
    if name.startswith('.'):
        if not package:
            msg = ("the 'package' argument is required to perform a relative "
                   "import for {!r}")
            raise TypeError(msg.format(name))
        for character in name:
            if character != '.':
                break
            level += 1
    return _bootstrap._gcd_import(name[level:], package, level)

您可以看到如果 name 不是以 . 开头,那么 if 部分不会被执行。您只有 return _bootstrap._gcd_import(name[level:], package, level)level=0 作为值

执行

现在让我们进入那个函数,它有下面的代码

def _gcd_import(name, package=None, level=0):
    """Import and return the module based on its name, the package the call is
    being made from, and the level adjustment.

    This function represents the greatest common denominator of functionality
    between import_module and __import__. This includes setting __package__ if
    the loader did not.

    """
    _sanity_check(name, package, level)
    if level > 0:
        name = _resolve_name(name, package, level)
    return _find_and_load(name, _gcd_import)

这里它再次执行 _find_and_load(name, _gcd_import),现在因为 level 是我们之前代码中的 0,所以根本没有传递或使用 package 参数通过 _find_and_load 方法。现在您可以通过下面的 运行

轻松验证这一点
import importlib
hello = importlib.import_module('hello', package='IAmNotAfolder')
hello.hello()

它会从基数 hello.py

打印 Hello World

因此,正如您所见,当名称不以 . 开头时,根本不会使用 package 参数,这是用于相对导入。这就是为什么您收到错误 No module named 'hello' 的原因,因为它试图从基本文件夹导入 hello.py,而不管您在包中有什么。

希望这个回答能让你更容易理解幕后发生的事情