exec_module() 无法执行导入其他模块的模块。

3

我有一个文件夹结构,类似于以下所示的结构:

main_folder
|
---> sub_folder_1
|    |
|    ---> main_script.py
|    |
|    ---> side_script.py
|
---> sub_folder_2
|    |
|    ---> main_script.py
|    |
|    ---> side_script.py
|
...

即在每个子文件夹中,我都有相同名称的文件。可能会发生这样的情况:对于某些子文件夹,主脚本从侧脚本导入一些对象,只需使用

import side_script

或者

from side_script import *

显然,每个主脚本都引用了同一文件夹中的侧脚本。现在,在完全不同的脚本中,我想要遍历子文件夹,加载主脚本,使用其中的某些内容并保存结果。由于在所有子文件夹中,我想要导入的主文件都以相同的方式命名,因此我需要仔细观察我正在导入哪个文件(来自哪个特定的子文件夹),所以我被建议使用importlib库,逐个加载模块,并使用exec_module()方法。
current_path = r'path\to\next\sub\folder'
spec = importlib.util.spec_from_file_location('main_script', os.path.join(current_path, 'main_script.py'))
executed_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(executed_module)

只要主脚本不导入侧边脚本,这将能够正常工作。但如果它们导入了侧边脚本,我会遇到以下错误:
ModuleNotFoundError: No module named 'side_module'

显然,当我直接运行任何主脚本时,导入这种类型的内容没有问题——只有在使用exec_module()方法导入主脚本时才会出现问题。我怀疑这与命名空间有关(exec_module()在新的命名空间中执行模块),但是我不知道如何使我正在执行的每个主脚本作为一个模块看到同一子文件夹中的脚本,而不改变主脚本本身。


尝试将导入语句更改为from sub_folder_1.side_script import *或类似的内容。在使用PyCharm时遇到了类似的问题。建议查阅有关相对导入和绝对导入的资料。 - MePsyDuck
我仍然遇到错误 - ModuleNotFoundError: No module named 'sub_folder_1'。但即使它能够正常工作,我实际上是从外部获取所有子文件夹的内容,我肯定更喜欢避免对它们进行任何修改。 - Jerry
你应该检查一下你的工作目录,模块文件可能无法从你执行运行脚本的位置访问。请查看这个链接:https://dev59.com/_LTma4cB1Zd3GeqP7oE- - MePsyDuck
3个回答

1
只要在每个子文件夹中都有一个名为__init__.py的文件,你就可以将该文件夹简单地添加到Python路径中。Python路径是在使用import语句时进行搜索的位置。例如:
import sys
sys.path.append(str(/full/path/to/sub_folder/))

我还没有找到更加优雅的方法来使用importlib来完成这个任务,或者指定包含你正在调用exec_module的模块的文件夹自动添加到Python路径中。 然而,由于你在操作Python路径,这意味着当你返回到开始执行的模块或启动另一个模块时,可能会导致意外行为。 因此,请查看这个答案,它展示了如何在每次调用后将Python路径重置为其原始状态的示例实现。


-1

这里有一个类似的问题在这里,然而建议的解决方案并不令人满意,因为它们暗示着要更改sys.path,而我认为你想要的是将一个模块仿佛它在你的工作文件夹中一样导入。

我的建议是在执行之前将你的模块添加到sys.modules中:

sys.modules[executed_module.__name__] = executed_module # add this line
spec.loader.exec_module(executed_module) # before executing the module

现在你应该能够在执行的模块中导入兄弟模块了:
from . import sibling

-1
你需要在所有文件夹中创建init.py来定义模块。
main_folder
|    |
---> __init__.py   
|    |
---> sub_folder_1
|    |
|    ---> __init__.py   
|    |
|    ---> main_script.py
|    |
|    ---> side_script.py
|
---> sub_folder_2
|    |
|    ---> __init__.py    
|    |
|    ---> main_script.py
|    |
|    ---> side_script.py
|
...

这仅适用于Python 3.3之前的版本。从v3.3开始,您不需要__init__.py来让Python将目录视为模块。 - MePsyDuck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接