Ошибки при динамическом импорте с использованием importlib в python3 - PullRequest
0 голосов
/ 11 мая 2018

Я пытался использовать 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')

папка / 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.Может кто-нибудь объяснить, почему это ожидаемое поведение?

Ответы [ 2 ]

0 голосов
/ 15 мая 2018

@ Ответ Махеша на 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 World из базы hello.py

Так что, как вы видите, параметр package не используетсявообще, когда имя не начинается с ., что для относительного импорта.Вот почему вы получаете ошибку No module named 'hello', потому что она пытается импортировать hello.py из базовой папки, независимо от того, что у вас в пакете.

Надеюсь, этот ответ облегчит вам понимание того, что происходит позадисцены

0 голосов
/ 12 мая 2018

Если первый аргумент импортируемого модуля является абсолютной ссылкой на модуль (без начального .), аргумент seond полностью игнорируется.

Чтобы импортировать модуль 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)
...