9

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

目录结构

主文件

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

你好.py

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

文件夹/hello.py

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

观察

如果我做

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

hello = importlib.import_module('hello')

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

如果我做

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

它从根文件夹导入文件夹/hello.pyhello 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 模块和包的理解有问题。有人可以解释为什么这是预期的行为吗?

4

2 回答 2

7

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

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

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

在你的情况下,这应该工作

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

根据经验,import package如果您想package用作第二个参数,应该可以工作。

from package import module

然后变成

importlib.import_module(module, package)
于 2018-05-12T05:15:41.870 回答
4

@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不以.then开头的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方法根本没有传递或使用参数。现在您可以通过以下运行轻松验证这一点0package_find_and_load

import importlib
hello = importlib.import_module('hello', package='IAmNotAfolder')
hello.hello()

它将Hello World从底座打印hello.py

因此,您可以看到,package当名称不以 开头时,根本不使用该参数.,这是用于相对导入。这就是为什么你得到一个错误No module named 'hello',因为它试图hello.py从基本文件夹导入,而不管你在包中有什么。

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

于 2018-05-15T06:45:41.477 回答