在包中运行模块,导入子包



我感觉这个问题以前被问过很多次,但不知何故,看似相关的问题的答案都没有让我完全理解发生了什么。

情况:我创建了一个包,在该包中我有另一个包,我希望能够直接运行所有这些包,用于测试目的,即:

main.py
package_1__init__.py
package_1module_1.py
package_1package_2__init__.py
package_1package_2module_2.py

其中 main.py:

from package_1.module_1 import func_1
func_1()

module_1.py:

from package_1.package_2.module_2 import func_2
def func_1():
func_2()
if __name__ == "__main__":
func_1()

module_2.py:

def func_2:
print ("This works")
if __name__ == "__main__":
func_2()

__init__.py为空。这允许我从根运行 main.py 并且它可以工作。我也可以从它自己的文件夹中运行module_2.py。但我不能跑module_1.py因为它抱怨没有package_1。这是有道理的,尽管从技术上讲,它本身就是package_1。但是如果我删除该前缀,它会破坏 main,这也是有道理的。

如何解决这个问题?我尝试通过以下方式替换module_1.py中的导入:

import .package_2.module_2

但这给我带来了一个我不完全理解的错误:

ModuleNotFoundError: No module named '__main__.package_2'; '__main__' is not a package

让所有包和模块按预期工作的正确方法是什么?这是应该在__init__.py解决的问题吗?还是我应该简单地避免像这样嵌套包并将它们全部安装(在提供 setup.py 之后(?

编辑:

@Blckknght建议运行:

python -m package_1.module_1
python -m package_1.package_2.module_1

从根目录。这是有效的,因为所有代码都按预期运行。我还在module_1.py中将导入更新为:

from .package_2.module_2 import func_2

最后,@jonilyn2730提供了通过将主体放在main((函数中来使其可以从其他脚本调用的建议。例如,module_1.py将是:

from package_1.package_2.module_2 import func_2
def func_1():
func_2()
def main():
func_1()
if __name__ == "__main__":
main()

在这个微不足道的示例中,它几乎没有区别,但它允许在其他脚本中执行此操作:

from package_1.module_1 import main
main()

因此,不再需要直接调用脚本,并且可以在单个脚本中组合多个运行。

没有好方法可以按文件名在包中运行模块,而不会中断包其他部分的所有导入。相反,您应该使用-m标志从顶级文件夹运行模块:

python -m package1.module1

以这种方式运行模块时,相对导入或绝对导入都应该有效(选择您喜欢的导入(。

但请注意,如果您的项目中有循环导入(以便您作为脚本运行的模块也从其他地方导入(,解释器最终将获得模块的两个副本,一个以__main__运行,另一个作为其正常名称运行。这可能非常尴尬,并导致令人困惑的错误。

如果您经常将模块作为脚本运行,则可能应该创建一个新的顶级脚本模块,该模块从包中导入模块并运行所需的代码(就像您在main.py中所做的那样(。这可以防止充满代码的模块可能存在两次,并且还可以让您从从.pyc文件加载缓存字节码中受益(这可能会使您的程序启动速度更快(。

最新更新