在 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
,而不管您在包中有什么。
希望这个回答能让你更容易理解幕后发生的事情
我一直在尝试将 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
,而不管您在包中有什么。
希望这个回答能让你更容易理解幕后发生的事情