Python函数导入方案咨询:多目录层级下跨脚本导入实现方法
嘿,作为Python新手碰到这种导入问题太正常了,我来一步步给你捋清楚,保证python script1.py和python script.py两种执行方式都能完美运行~
第一步:完善包结构(添加__init__.py)
首先,给你的package1和package2都加上空的__init__.py文件。虽然Python 3.3+支持没有这个文件的命名空间包,但显式添加它有两个关键好处:
- 明确告诉Python这是一个可导入的包,避免潜在的命名冲突
- 兼容更早的Python版本,同时让项目结构更符合传统Python包规范
调整后的目录结构如下:
root ┣ 📂 modules ┃ ┣ 📂 package1 ┃ ┃ ┣ 📜 __init__.py ┃ ┃ ┗ 📜 script1.py ┃ ┗ 📂 package2 ┃ ┃ ┣ 📜 __init__.py ┃ ┃ ┗ 📜 script2.py ┗ 📜 script.py
第二步:用「绝对导入+动态添加根路径」实现兼容
问题的核心是:直接运行script1.py时,Python的模块搜索路径(sys.path)里没有root目录,导致找不到modules.package2;而运行script.py时,root默认就在sys.path里,导入很顺畅。
所以我们需要在script1.py开头添加一段代码,动态把root目录加入sys.path,之后就能安心用绝对导入了。
1. 编写script2.py(示例)
先把基础函数写好:
# modules/package2/script2.py def function2(): print("Running function2 from script2.py")
2. 编写script1.py(关键部分)
在开头加入动态添加路径的代码,再用绝对导入引入function2:
# modules/package1/script1.py import sys from pathlib import Path # Python 3.4+推荐用pathlib,代码更简洁 # 动态获取root目录:当前脚本目录 → 上级(package1)→ 上级(modules)→ 上级(root) root_dir = Path(__file__).parent.parent.parent.resolve() # 如果root不在搜索路径里,就加进去 if root_dir not in sys.path: sys.path.append(str(root_dir)) # 现在可以用绝对导入了 from modules.package2.script2 import function2 def function1(): print("Running function1 from script1.py") function2() # 调用script2里的函数 # 直接运行script1.py时执行 if __name__ == "__main__": function1()
如果你用的是Python 3.4以下版本,可以用os.path替代pathlib:
# 兼容旧版本的路径代码 import sys import os root_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) if root_dir not in sys.path: sys.path.append(root_dir)
3. 编写script.py
因为script.py在root目录,默认就在sys.path里,直接用绝对导入即可:
# script.py from modules.package1.script1 import function1 function1()
验证两种执行场景
- 直接运行
script1.py:进入modules/package1目录,执行python script1.py,会输出:Running function1 from script1.py Running function2 from script2.py - 运行
script.py:进入root目录,执行python script.py,输出和上面完全一致
为什么不推荐相对导入?
相对导入(比如from ..package2.script2 import function2)只能在包内部被当作模块导入时生效,如果直接运行script1.py(此时它是__main__模块),Python会抛出ValueError: attempted relative import beyond top-level package的错误,所以这种方式没法兼顾两种执行场景,不如我们上面的方案可靠。
内容的提问来源于stack exchange,提问作者Kevin

