Python 3 中的动态导入与相对导入

2 投票
1 回答
2563 浏览
提问于 2025-04-16 12:17

我有一个Python 3的项目,我在这个项目中需要从磁盘上动态加载模块,使用的是imp.load_module。但是,我遇到了一个问题,就是当在动态加载的模块中进行相对导入时,会失败。

根据我所了解的,默认的导入器在确定导入路径时只会使用__file____path____package____name__这几个属性。然而,我在下面的代码中验证了这些属性,但在动态导入时仍然失败。(在解释器中更新sys.path后导入是可以成功的)

# File structure:
# [root]
#  ├─ __init__.py
#  ├─ board.py
#  └─ test.py

# Contents of 'board.py':
import os, sys
import root  # Already imported... just need a reference

ROOT_DIR = os.path.dirname(root.__file__)
assert root is sys.modules['root']
assert root.__package__ is None
assert root.__name__ == 'root'
assert root.__file__ == os.path.join(ROOT_DIR, '__init__.py')
assert not hasattr(root, '__path__')

xx = object()
assert xx is sys.modules['root.board'].xx
assert __package__ is None
assert __name__ == 'root.board'
assert __file__ == os.path.join(ROOT_DIR, 'board.py')
assert not hasattr(sys.modules['root.board'], '__path__')

assert os.path.isfile(os.path.join(ROOT_DIR, 'test.py'))
from . import test  # ImportError('cannot import name test',)

但是,如果我在失败的导入之前修改sys.path并重新导入当前包,就能成功:

oldroot = root
del sys.modules['root']
sys.path.append(os.path.dirname(ROOT_DIR))
import root
from . import test  # No error here

而且,上面提到的四个重要属性在新旧包中是相同的:

assert oldroot.__package__ == root.__package__
assert oldroot.__name__ == root.__name__
assert oldroot.__file__ == root.__file__
assert not hasattr(root, '__path__')

这意味着__package____name____file____path__可能不是全部原因。还有其他什么属性是Python用来定位导入的呢?我忽略了什么,导致导入失败?

1 个回答

1

没有看到 root.__path__ 这个值有点可疑(这意味着Python不把 root 当作一个包)。

另外,两个 load_module() 的调用都是用类型值 1(表示模块)来进行的,而第一个应该是类型 5(表示包),这也让人觉得很奇怪。

你是不是在用 imp.find_module() 来确定 imp.load_module() 调用的正确输入呢?

对于一个包,传给 imp.load_module() 的输入应该像这样:

# demo is a package in the current directory for this example
>>> info = imp.find_module('demo')
>>> info
(None, 'demo', ('', '', 5))
>>> demo = imp.load_module('demo', *info)
>>> demo.__path__
['demo']

撰写回答