无需添加至sys.path导入第三方Python项目的方法
我尝试在自有代码中导入一个第三方Python项目,此处称其为“项目”而非“包”,因为它仅被设计为独立运行,而非供他人导入到其他环境中使用。
项目结构与依赖
该第三方项目(ProjectA)的结构如下:
ProjectA/ |- utils/ |- tools.py |- lib/ |- functions.py |- main.py
文件间的依赖关系:
main.py中:from lib.functions import xxxlib/functions.py中:from utils.tools import yyy
具体逻辑:
- 运行
main.py需要从lib/functions.py导入函数xxx; - 调用
lib/functions.py里的xxx需要用到utils.tools.py中的函数yyy。
常规运行方式的局限性
在ProjectA的顶层目录运行main.py时,lib和utils模块在当前层级可见,因此可以正常运行。另外,也可以把path_to_ProjectA添加到sys.path中,在外部环境调用main.py里的函数,同样能正常运行。
但这种方法存在问题:如果我需要同时使用另一个包含utils子模块的ProjectB,或者自有项目中已经有utils子模块,将两个项目都加到sys.path后,只会有一个utils子模块能被识别,导致冲突。
我曾考虑将该项目改造为“标准”包,比如在各层级添加__init__.py、修改导入方式等,但这种方法对大型项目不实用——不仅需要充分理解项目结构设计,还要修改大量导入语句,成本太高。
补充示例(20220819)
为更清晰说明问题,以下是极简示例,目标是在ProjectA外部调用ProjectA.lib.functions.xxx,且不将ProjectA添加至sys.path。
(a) 在ProjectA内部调用xxx
此时ProjectA已添加空的__init__.py,结构如下:
ProjectA/ |-- lib | |-- __init__.py # 空文件 | `-- functions.py |-- utils |-- __init__.py # 空文件 `-- tools.py `-- main.py
ProjectA/lib/functions.py内容:
from utils.tools import yyy def xxx() : print("This is xxx().") print("Now xxx() calls yyy().") yyy()
ProjectA/utils/tools.py内容:
def yyy() : print("This is yyy().")
ProjectA/main.py内容:
from lib.functions import xxx def main() : print("This is main().") print("Now calling xxx().") xxx() if __name__=='__main__' : main()
在ProjectA目录下运行python main.py,结果正常:
This is main(). Now calling xxx(). This is xxx(). Now xxx() calls yyy(). This is yyy().
(b) 添加ProjectA到sys.path,在外部调用xxx
假设存在与ProjectA同级的MyProject目录:
./ |-- MyProject | `-- my_script.py | `-- ProjectA |-- ...
MyProject/my_script.py内容:
import sys sys.path.append("../ProjectA") from lib.functions import xxx def main() : print("This is outside of ProjectA.") print("Now calling xxx().") xxx() if __name__=='__main__' : main()
运行成功,输出:
This is outside of ProjectA. Now calling xxx(). This is xxx(). Now xxx() calls yyy(). This is yyy().
(c) 将ProjectA作为子目录,直接外部调用xxx(报错场景)
将ProjectA放入外部目录的thirdparty子文件夹中:
MyProject2/ |-- my_script.py `-- thirdparty `-- ProjectA |-- ...
MyProject2/my_script.py内容:
from thirdparty.ProjectA.lib.functions import xxx def main() : print("This is outside of ProjectA.") print("Now calling xxx().") xxx() if __name__=='__main__' : main()
运行报错:
Traceback (most recent call last): File "my_script.py", line 1, in <module> from thirdparty.ProjectA.lib.functions import xxx File "/workspace/MyProject2/thirdparty/ProjectA/lib/functions.py", line 1, in <module> from utils.tools import yyy ModuleNotFoundError: No module named 'utils'
核心问题
如何在不将第三方Python项目添加至sys.path的前提下,从外部导入该项目(即使该项目仅被设计为在其顶层目录运行)?
内容的提问来源于stack exchange,提问作者tommyc

