web-dev-qa-db-ja.com

Python3でimportlibを使用した動的インポート中のエラー

私はpython3(3.6)でimportlibを使おうとしています。

ディレクトリ構造

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モジュールとパッケージの理解に何か問題があると確信しています。これが予期される動作である理由を誰かが説明できますか?

5
Umang Gupta

インポートされるモジュールの最初の引数が絶対モジュール参照(先頭に.がない)の場合、seond引数は完全に無視されます。

モジュールaを別のモジュールbと比較してインポートするには、次を使用する必要があります

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

あなたの場合、これはうまくいくはずです

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

経験則として、2番目の引数としてpackageを使用する場合は、import packageが機能するはずです。

from package import module

その後、

importlib.import_module(module, package)
5
Mahesh

@Maheshの答えは100%正確で正確ですが、理解を深めるために1レベル深くする必要があると思います。

以下は_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部分が実行されないことがわかります。 _level=0_を値として実行されるreturn _bootstrap._gcd_import(name[level:], package, level)があります

それでは、以下のコードを持つ関数に取り掛かりましょう。

_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_が発生する理由です。

この回答により、舞台裏で何が起こっているのかを理解しやすくなることを願っています

2
Tarun Lalwani