如何在多目录项目中正确导入 python 模块?
Posted
技术标签:
【中文标题】如何在多目录项目中正确导入 python 模块?【英文标题】:How do I properly import python modules in a multi directory project? 【发布时间】:2021-03-13 23:49:35 【问题描述】:我有一个基本设置如下的 python 项目:
imptest.py
utils/something.py
utils/other.py
以下是脚本中的内容:
imptest.py
>#!./venv/bin/python
import utils.something as something
import utils.other as other
def main():
"""
Main function.
"""
something.do_something()
other.do_other()
if __name__ == "__main__":
main()
something.py
>#import other
def do_something():
print("I am doing something")
def main():
"""
Main function
"""
do_something()
#other.do_other()
if __name__ == "__main__":
main()
其他.py
>def do_other():
print("do other thing!")
def main():
"""
Main function
"""
do_other()
if __name__ == "__main__":
main()
imptest.py 是主文件,它偶尔会运行并调用 utils 函数。
如您所见,我在“something.py”中注释掉了一些行,我正在导入“其他”模块进行测试。
但是当我想测试something.py中的某些功能时,我必须运行文件something.py并取消对导入行的注释。
这感觉有点笨拙。
如果我离开了
import other
取消注释并运行 imptest.py,我得到这个错误:
Traceback (most recent call last):
File "imptest.py", line 5, in <module>
import utils.something as something
File "...../projects/imptest/utils/something.py", line 3, in <module>
import other
ModuleNotFoundError: No module named 'other'
有什么更好的方法?
【问题讨论】:
测试你的 sn-ps。这些扩展会搞砸事情 您介意发布minimal, reproducible example 而不是描述假设案例吗? “一切正常。”:在这种情况下,粘贴您的实际代码 嘿,我添加了更多上下文/代码。请看看这是否更有用。 【参考方案1】:这里的问题是路径,考虑这个目录结构
main
- utils/something.py
- utils/other.py
imptest.py
当您尝试使用相对路径将other
导入到something.py
时,您会执行类似from . import other
的操作。这在您执行 $ python something.py
时会起作用,但在您运行 $ python imptest.py
时会失败,因为在第二种情况下,它会搜索不存在的 main/other.py。
所以为了解决这个问题,我建议你为 something.py 和 other.py 编写单元测试并使用$ python -m
(mod) 命令运行它们。 (我强烈推荐这种方法)
但是....不推荐这种方法)
import sys, os
sys.path.append(os.getcwd()) # Adding path to this module folder into sys path
import utils.other as other
def do_something():
print("I am doing something")
def main():
"""
Main function
"""
do_something()
other.do_other()
if __name__ == "__main__":
main()
这里有一些参考资料可以帮助你更好地理解:
Unit testing in python Absolute vs Relative Imports in python【讨论】:
是的,我在研究这个时得出了一些类似的结论。我可以运行实用程序。使用 python -m utils.something 文件,它不会引发错误。我现在必须弄清楚测试。我只是想快速测试 utils 脚本函数,而不使用“断言”并在使用函数之前尝试确定函数的输出你知道我的意思吗?感谢您的帮助。【参考方案2】:PavanSkipo 已使用相对路径 here 解决了 ImportError
问题,但这些文件只能通过 python -m utils.something
作为模块调用 - 而 python utils/something.py
将引发错误。在我看来,使用sys.path
作为 PavanSkipo 的解决方法并不理想1。
OP 对 if __name__=='__main__'
语句的使用表明文件旨在以某些方式直接运行 - 可能作为脚本 2。在这种情况下,我们应该使用绝对导入和pip install -e .
使用这种方法,文件既可以作为模块运行,也可以作为脚本单独运行。换句话说,以下所有python utils/something.py
、python -m utils.something
和import utils.something
都可以正常工作。
添加setup.py
setup.py
imptest.py
utils/something.py
utils/other.py
setup.py
的最少内容
from setuptools import setup, find_packages
setup(name='utils', version='0.1', packages=find_packages())
使用pip install -e .
在开发模式下安装后,您应该像在imptest.py
中一样在utils/something.py
中使用绝对导入
import utils.other
...
1 被测试的代码最好能反映正在使用的代码的状态。
2 好吧,也许可以作为入口点 - 但在这种情况下,它应该已经作为一个包安装了
【讨论】:
【参考方案3】:我想你要找的是这个。为了能够导入库并将其用作可执行文件,您可以这样做。首先,您应该将utils
制作成一个包。这意味着在其中创建__init__.py
。
内容应该是这样的:
→ cat utils/__init__.py
import os
import sys
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
这将使导入语句在imptest.py
中作为from utils import something, other
工作,在something.py
中也作为import other
工作。
最后一段如下:
→ tree
.
|-imptest.py
|-utils
| |-__init__.py
| |-other.py
| |-something.py
→ cat imptest.py
#!./venv/bin/python
from utils import something, other
def main():
"""
Main function.
"""
something.do_something()
other.do_other()
if __name__ == "__main__":
main()
→ cat utils/something.py
import other
def do_something():
print("I am doing something")
def main():
"""
Main function
"""
do_something()
other.do_other()
if __name__ == "__main__":
main()
→ python3 imptest.py
I am doing something
do other thing!
→ python3 utils/something.py
I am doing something
do other thing!
这个技巧使两个 import 语句都有效。它基本上将utils
目录路径添加到搜索路径中,而不管它位于何处。因此,import other
将起作用。 __init__.py
使utils
目录成为package;
【讨论】:
不,运行 imptest.py 失败,在 something.py 中导入 other 导致模块 other 在 something.py 中找不到。【参考方案4】:您需要在主目录中添加一个__init__.py
文件,以便python 知道它是一个包。然后你可以像这样从 .other 导入函数中添加一个导入语句
【讨论】:
我修改了帖子。我不认为这正是我所追求的解决方案。 如果我不想导入函数而是导入整个模块怎么办?我想这不是一个好主意? 这不能解决 - 与 ModuleNotFound 错误无关以上是关于如何在多目录项目中正确导入 python 模块?的主要内容,如果未能解决你的问题,请参考以下文章