同一个 probe.py,不设 PYTHONPATH 时返回码 1、报 ModuleNotFoundError;
设上就返回 0、打出 VALUE = 38。两个目录里放同名模块,PYTHONPATH 谁在前就 import 谁。
上一节装好了 Python,这一节解决「为什么装好了还是找不到」。答案几乎总在环境变量里。
一、PATH 和 sys.path 是两条路
这两个名字长得像,管的事完全不同:
- PATH 管敲命令:你打 python,系统按 PATH 的条目一个个找,找到第一个就用。
- sys.path 管 import:你写 import requests,Python 按这个列表一个个找同名的 .py 或包目录。
改 PATH 不会让 import 变好,改 PYTHONPATH 也不会让命令找得到。
搞混这两个,是新手最容易卡住的地方。
二、PYTHONPATH 的作用
PYTHONPATH 是专门给 import 用的:里面的每个目录都会被插到 sys.path 前面(在标准库之前、
脚本目录之后)。
Demo 把一个 mytool.py 放进一个不在 sys.path 里的中文目录,然后起子进程跑 import mytool。
不设 PYTHONPATH:返回码 1,报 ModuleNotFoundError: No module named 'mytool'。
设上:返回码 0,打出 import 到了,VALUE = 38。
同一份代码、同一个解释器,只差一个环境变量,一个能跑一个不能。这就是为什么别人能跑你不能跑。
三、顺序决定谁生效
更麻烦的情况是同名:两个目录里都有 same.py,内容不同,import 到哪个?
Demo 的答案很干脆:PYTHONPATH 里谁在前就是谁。A 在前打出 A 版,B 在前打出 B 版。
所以名字冲突是静默的错误——不报错,只是用了错的那个文件。
排查这种问题只能靠打印 模块.__file__。
四、本机的 sys.path 长什么样
Demo 列出本进程前 5 项:第 0 项是脚本所在目录,接着是一个 shim 目录,然后是 python313.zip、
DLLs、Lib——一共 11 项。
注意第 0 项就是当前脚本所在的目录。
这解释了一个常见现象:同一个文件名,你在不同目录下运行结果不一样。
python313.zip 出现在列表里,说明标准库有一部分是打包成 zip 的。
这也是为什么有的模块「明明没装却能 import」——它在标准库里。
排查口诀:先打 sys.path,再看 __file__。
两行代码,能解决九成的「模块找不到」和「用错了模块」。
五、动手 Demo:加不加 PYTHONPATH,同一个 import 两种命
在临时目录里造一个模块和一个探针脚本,
用子进程分别在不带 PYTHONPATH 和带 PYTHONPATH 的环境下各跑一次;
再造两个同名模块,交换 PYTHONPATH 的顺序,看 import 到的是哪一个。
最后打印本进程 sys.path 的前五项。
env = dict(os.environ)
env.pop('PYTHONPATH', None)
r1 = subprocess.run([sys.executable, probe], capture_output=True,
text=True, env=env)
env['PYTHONPATH'] = mod_dir
r2 = subprocess.run([sys.executable, probe], capture_output=True,
text=True, env=env)
for order in ((dir_a, dir_b), (dir_b, dir_a)):
env['PYTHONPATH'] = os.pathsep.join(order)
subprocess.run([sys.executable, probe2], env=env)
实跑输出:
--------------------------------------------------------------------
环境变量到底管什么:同一个 import,加不加 PYTHONPATH 两种命
--------------------------------------------------------------------
模块放在 一个不在 sys.path 里的中文目录
不设 PYTHONPATH 返回码 1
报错最后一行 ModuleNotFoundError: No module named 'mytool'
设了 PYTHONPATH 返回码 0
标准输出 import 到了,VALUE = 38
PYTHONPATH 顺序 A 在前 A 版
PYTHONPATH 顺序 B 在前 B 版
序 本进程 sys.path 这一项
----------------------------------------------------------------------------
0 I:\Desktop\自我教学\.workbuddy\scripts\ch38_dev
1 D:\WorkBuddy\resources\app.asar.unpacked\cli\vendor\shim
2 C:\Users\admin\.workbuddy\binaries\python\versions\3.13.12\python313.zip
3 C:\Users\admin\.workbuddy\binaries\python\versions\3.13.12\DLLs
4 C:\Users\admin\.workbuddy\binaries\python\versions\3.13.12\Lib
这 11 项谁在前面,谁就先生效——不是「都一样」。
不设 PYTHONPATH 那次返回码是 1,
最后一行是 ModuleNotFoundError: No module named 'mytool'——报错原文直接告诉你缺哪个名字。
设上之后返回码 0,打出 import 到了,VALUE = 38。
环境变量是进程级的,所以必须用子进程验证,在本进程里改 os.environ 对已经启动的解释器没用。
同名模块那一组:A 在前打 A 版,B 在前打 B 版。不报错、不警告,只是悄悄用了另一个文件。
本进程 sys.path 共 11 项,第 0 项是脚本所在目录,第 1 项是个 shim 目录。
这两个不是标准库给的,是启动方式带进来的。
30 秒实验
- 写两行
import sys; print(sys.path)和import 某模块; print(某模块.__file__),存成备忘片段。 - 把项目根目录加进
PYTHONPATH,看能不能省掉一堆sys.path.append。 - 想一想:为什么
PYTHONPATH在标准库之前生效,会带来风险?
常见误区
- 「PATH 加进去就能 import 了」——那是找命令,不是找模块。
- 「装了两个同名包也没事,反正都能 import」——只会用一个,另一个永远不生效。
- 「报 ModuleNotFoundError 就是没装」——也可能装了但不在
sys.path里。
自测三问(点开对答案)
1. `PATH` 和 `PYTHONPATH` 分别管什么? —— 前者管命令行找可执行文件,后者管 import 找模块。 2. 两个目录有同名模块,谁生效? —— `PYTHONPATH` 里排在前面的那个,且不会报错。 3. 排查「模块找不到」先看什么? —— 打印 `sys.path`,再打印目标模块的 `__file__`。环境配好,可以写第一行代码了——从最简单的「打出一串英文字符」开始。
- ☐ (本节完,下一节 3.2.1 使用Python基础语法:输出英文字符)