前三篇我们讲了 sys.path、两种 import 写法、PyCharm 配置。方案对比表里有一行一直没展开,那就是 PYTHONPATH 环境变量。
这篇把它讲清楚:它和 sys.path 的关系、怎么设、作用域有多大、什么时候该用什么时候不该用。
一、PYTHONPATH 和 sys.path 是什么关系?
回顾第一篇,sys.path 的构成:
| 顺序 | 来源 |
|---|---|
| 1 | 脚本所在目录(或当前工作目录) |
| 2 | PYTHONPATH 环境变量 |
| 3 | 标准库目录 |
| 4 | site-packages(第三方包) |
PYTHONPATH 是一个操作系统级别的环境变量。Python 启动时会读取它的值,把里面列出的目录插入到 sys.path 的第 1 项之后。
换句话说,PYTHONPATH 是在 Python 进程外部,预先往 sys.path 里"注入"路径的一种方式。
二、基本用法
还是用第一篇的项目结构:
my_project/
├── main.py
├── core/
│ ├── __init__.py
│ └── engine.py
└── utils/
├── __init__.py
└── helpers.py
如果不设 PYTHONPATH,在 core/ 目录下运行会报错:
cd my_project/core
python engine.py
# ModuleNotFoundError: No module named 'utils'
设了 PYTHONPATH 指向项目根目录,就能找到:
cd my_project/core
PYTHONPATH=/home/my_project python engine.py
# ✅ 能找到 utils
打印 sys.path 就能看到效果:
import sys
for i, p in enumerate(sys.path):
print(f"[{i}] {p}")
[0] /home/you/my_project/core ← 脚本所在目录
[1] /home/you/my_project ← PYTHONPATH 注入的
[2] /usr/lib/python3.11 ← 标准库
[3] /usr/lib/python3.11/lib-dynload
[4] /home/you/.local/lib/python3.11/site-packages
三、怎么设?三种作用域
这是本文的重点。环境变量的作用域取决于你在哪里设、怎么设。
3.1 单条命令:只对这一次运行生效
PYTHONPATH=/home/you/my_project python engine.py
写在命令最前面,只影响紧跟其后的这一条命令。命令结束,这个变量就消失了。
PYTHONPATH=/home/you/my_project python engine.py # ✅ 生效
python engine.py # ❌ 没了
- 适用场景:临时跑一个脚本,不想污染环境。
Windows 的 CMD 不支持这种语法。PowerShell 可以用
$env:PYTHONPATH="..."; python engine.py,但语义不同,变量会留在当前会话里。
3.2 当前终端会话:关掉终端就没了
export PYTHONPATH=/home/you/my_project
export 之后,当前终端窗口里所有后续命令都能看到这个变量。关掉终端(或开一个新终端),它就不存在了。
export PYTHONPATH=/home/you/my_project
python engine.py # ✅
python another.py # ✅ 同一个终端,都生效
# 关掉终端,开一个新的
python engine.py # ❌ 变量没了
Windows CMD 对应的是 set:
set PYTHONPATH=C:\Users\you\my_project
python engine.py
同样只在当前 CMD 窗口有效。
适用场景:一次调试会话要反复运行多个脚本。
3.3 永久生效:写进 Shell 配置文件
如果你希望每次打开终端都自动设好,把 export 写进 Shell 的配置文件:
| Shell | 配置文件 |
|---|---|
| Bash | ~/.bashrc 或 ~/.bash_profile |
| Zsh(macOS 默认) | ~/.zshrc |
| Fish | ~/.config/fish/config.fish |
# 在 ~/.bashrc 末尾加一行
export PYTHONPATH=/home/you/my_project:$PYTHONPATH
注意末尾的 :$PYTHONPATH,它表示追加,保留已有的值。不加这个后缀会覆盖掉之前设的所有路径。
Windows 要通过系统设置:设置 → 系统 → 关于 → 高级系统设置 → 环境变量,在用户变量或系统变量里添加 PYTHONPATH。
适用场景:极少。大多数情况你不应该永久设置 PYTHONPATH,下一节解释为什么。

三种设置方式的作用域逐级递增:单条命令的变量随命令结束即消亡,最安全也最推荐;export 到终端会话则在关闭窗口时失效,适合一轮调试;写入 Shell 配置文件则全局永久生效,方便的同时也最容易引发跨项目的模块污染。日常开发中,作用域越小越安全。
四、多个路径怎么写?
PYTHONPATH 可以包含多个目录,用分隔符隔开:
- Linux / macOS:冒号
: - Windows:分号
;
# Linux / macOS
export PYTHONPATH=/path/to/project_a:/path/to/project_b
# Windows CMD
set PYTHONPATH=C:\project_a;C:\project_b
Python 会按顺序把这些目录加入 sys.path。顺序有关系,如果两个目录下有同名模块,排在前面的会被优先找到。
五、什么时候该用,什么时候不该用?
5.1 适合用的场景
(1) CI / CD 流水线
你没有 IDE,不方便改代码里的 import,也不想在项目里加 setup.py。在流水线脚本里设一行 PYTHONPATH 是最轻量的方案:
# GitHub Actions 示例
- name: Run tests
env:
PYTHONPATH: ${{ github.workspace }}/src
run: pytest
(2) 容器启动脚本
Dockerfile 或 entrypoint 里设一次,作用域天然限定在容器内:
ENV PYTHONPATH=/app/src
一次性脚本调试,用单条命令的方式,跑完就丢,不留痕迹。
5.2 不适合用的场景
(1) 日常开发
你同时开发多个项目,永久设置的 PYTHONPATH 会让项目 A 的模块"泄漏"到项目 B 里。更糟的情况是两个项目有同名模块,Python 静默加载了错误的那个,你调试半天都想不到是路径问题。
(2)正式项目的长期方案
PYTHONPATH 是隐式的。新同事 clone 你的项目,跑不起来,因为他不知道要设这个变量。这种"我的电脑上能跑"的问题,用 pip install -e . 可以彻底避免。
(3)替代 python -m
如果你的问题仅仅是运行方式不对,改用 python -m 就能解决,没必要动环境变量。
六、PYTHONPATH vs 其他方案
| 方案 | 生效范围 | 是否需要改代码 | 可移植性 | 推荐度 |
|---|---|---|---|---|
python -m | 当次运行 | 否 | 高 | ⭐⭐⭐ |
pip install -e . | 当前虚拟环境 | 需要 pyproject.toml | 高 | ⭐⭐⭐ |
PYTHONPATH(单条命令) | 当次运行 | 否 | 中 | ⭐⭐ |
PYTHONPATH(export) | 当前终端 | 否 | 低 | ⭐⭐ |
PYTHONPATH(永久) | 所有终端 | 否 | 低 | ⭐ |
sys.path.append | 当次运行 | 是 | 低 | ⭐ |
一句话总结:PYTHONPATH 适合"不方便改代码、也不方便改运行方式"的场景。如果你能选运行方式,用 python -m;如果是正式项目,用 pip install -e .。
七、排查技巧:确认 PYTHONPATH 是否生效
在 Python 外部查看:
echo $PYTHONPATH # Linux / macOS
echo %PYTHONPATH% # Windows CMD
echo $env:PYTHONPATH # Windows PowerShell
在 Python 内部验证:
import os
print(os.environ.get('PYTHONPATH', '未设置'))
如果你设了 PYTHONPATH 但 import 仍然失败,按这个顺序排查:
- 打印
sys.path:确认你设的路径确实出现在列表里 - 检查路径拼写:多一个
/、少一层目录都会导致失败 - 检查作用域:你是在当前终端 export 的,还是写进了配置文件?新开的终端能看到吗?
- 虚拟环境:激活虚拟环境可能会重置
PYTHONPATH,确认激活后变量还在
八、总结
PYTHONPATH是往sys.path注入路径的外部手段,Python 启动时读取,插入到sys.path[0]之后- 三种作用域:单条命令(最安全)、当前终端会话、永久写入配置文件(慎用)
- 适合 CI、容器、一次性调试,不方便改代码或运行方式时,它是最轻量的选择
- 不适合日常开发和正式项目,隐式依赖,容易产生跨项目污染和"在我电脑上能跑"的问题
- 优先考虑
python -m和pip install -e .,它们更显式、更可移植、更不容易出意外


4904

被折叠的 条评论
为什么被折叠?



