Python脚本与模块分离目录下的导入问题求助
这个问题我之前也碰到过,核心原因是Python的模块搜索路径(sys.path)在两种运行方式下不一样:
- 当你直接执行
python bin/script1.py时,Python会把脚本所在的bin/目录添加到sys.path的最前面,而你的lib目录在app/根目录下,不在bin/的子目录里,所以自然找不到。 - 但你直接启动解释器时,当前工作目录(也就是
app/根目录)会被自动加入sys.path,所以import lib.module1能正常工作。
下面给你几个可行的解决方案,按推荐程度排序:
1. 使用-m参数运行脚本(最推荐,符合Python包规范)
你可以把app目录当作一个Python包,用模块方式运行脚本,而不是直接执行.py文件。在app/根目录下执行:
python -m bin.script1
注意这里不需要加.py后缀。这种方式下,Python会把当前工作目录(app/)加入sys.path,此时bin和lib都是app下的子包,你可以直接在脚本里写:
from lib.module1 import module1a
完全不会有导入问题,而且这是Python官方推荐的包内脚本运行方式,避免了路径混乱。
2. 在脚本开头手动添加根目录到sys.path
如果不想改运行命令,可以在script1.py和script2.py的最开头添加一段代码,把app/根目录加入Python的搜索路径:
import sys import os # 获取脚本所在目录(bin/),然后向上跳一级到app/根目录 root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) # 把根目录插入到sys.path的最前面,确保优先搜索 sys.path.insert(0, root_dir) # 现在就能正常导入了 from lib.module1 import module1a
这段代码的好处是不需要依赖环境变量或特殊运行命令,脚本可以单独执行,但缺点是如果目录结构变动,可能需要修改这段代码。
3. 设置PYTHONPATH环境变量
在运行脚本前,把app/根目录设置到PYTHONPATH环境变量中,Python会自动从这个路径搜索模块:
Linux/macOS:
export PYTHONPATH=$(pwd) python bin/script1.py
Windows命令提示符:
set PYTHONPATH=%cd% python bin\script1.py
Windows PowerShell:
$env:PYTHONPATH = (Get-Location).Path python bin\script1.py
这种方式不需要修改代码,但每次运行都要先设置环境变量,或者可以把它加到你的shell配置文件(比如.bashrc、.zshrc)里,一劳永逸。
4. 将项目安装为可编辑包(适合长期开发)
如果这是一个需要长期维护的项目,推荐把app目录安装为Python的可编辑包。首先在app/根目录下创建一个setup.py文件:
from setuptools import setup, find_packages setup( name="myapp", version="0.1", packages=find_packages(), )
然后在app/根目录下执行:
pip install -e .
这个命令会把你的项目安装到Python的包目录中,但保持源码的链接,修改源码后不需要重新安装。之后不管你在哪个目录运行脚本,都能直接导入lib模块,非常方便。
为什么相对导入不行?
你尝试的from ..lib.module1 import module1a属于相对导入,但相对导入只有在脚本被当作包的一部分时才有效。当你直接执行python bin/script1.py时,这个脚本被视为"顶层脚本",Python不认为它属于任何包,所以相对导入会报错ValueError: Attempted relative import in non-package。只有用-m参数运行,或者把脚本作为模块导入时,相对导入才能正常工作。
内容的提问来源于stack exchange,提问作者ewok

