第 2 天|工作目录、文件路径、模块引用与软件包¶
日期: 2026 年 7 月 21 日
核心内容: 工作目录、绝对与相对路径、Jupyter 与 .py 的路径差异、sys.path、模块导入、pip
路径问题是数据分析和编程竞赛中最常见的非语法错误。代码没有写错,但启动位置不同, 可能读取了错误文件、找不到 CSV、把结果写到意外目录,或者无法导入本地模块。
终端命令与 Python 代码¶
| 类型 | 示例 | 输入位置 |
|---|---|---|
| 终端命令 | python --version |
终端 |
| 安装命令 | python -m pip install pandas |
终端 |
| Python 代码 | import pandas |
.py 或 Notebook 代码单元 |
| 命令输出 | Python 3.x.x |
电脑自动显示,不需要输入 |
终端提示符、当前路径和命令执行结果都不应抄进 .py 文件。
一、必须区分的四个位置¶
| 位置 | Python 中的检查方法 | 决定什么 |
|---|---|---|
当前工作目录 cwd |
Path.cwd() |
相对数据路径从哪里开始计算 |
| 当前脚本或模块的位置 | Path(__file__).resolve() |
当前 .py 文件实际存放在哪里 |
| 模块搜索路径 | sys.path |
import 会去哪些目录寻找模块 |
| Python 解释器的位置 | sys.executable |
当前使用哪个环境、去哪个环境找第三方包 |
这四个位置可能相同,也可能完全不同。最关键的两条规则是:
open("data/a.csv")、pd.read_csv("data/a.csv")等相对文件路径,默认从 cwd 开始;import helper不是按照 cwd 单独判断,而是依次搜索sys.path中的目录。
相对文件路径并不自动相对于当前 .py 文件。 这是本节最重要的结论。
二、检查当前工作目录¶
在 PowerShell 中:
Get-Location
Get-ChildItem
Set-Location "D:\Python暑期训练\第01周"
在 CMD 中:
cd
dir
cd /d "D:\Python暑期训练\第01周"
在 Python 或 Notebook 中:
from pathlib import Path
print(Path.cwd())
cwd 是进程的“出发位置”。通过终端启动程序时,它通常继承终端当前所在的目录;
通过编辑器或 Notebook 启动时,则由编辑器、工作区和内核启动方式共同决定。
因此不要凭感觉判断,运行前直接打印 Path.cwd()。
from pathlib import Path
import sys
print("当前工作目录 cwd:", Path.cwd())
print("Python 解释器:", sys.executable)
print("sys.path[0]:", sys.path[0])
if "__file__" in globals():
print("当前脚本文件:", Path(__file__).resolve())
else:
print("__file__:Notebook 中通常没有这个变量")
三、绝对路径与相对路径¶
假设项目结构为:
D:\Python暑期训练\路径演示
├── data
│ └── sales.csv
├── notebooks
│ └── analysis.ipynb
├── scripts
│ └── main.py
├── src
│ ├── __init__.py
│ └── cleaner.py
└── outputs
| 写法 | 类型 | 实际含义 |
|---|---|---|
D:\Python暑期训练\路径演示\data\sales.csv |
绝对路径 | 从盘符开始,位置完整 |
data\sales.csv |
相对路径 | cwd/data/sales.csv |
..\data\sales.csv |
相对路径 | cwd 的上一级目录中的 data/sales.csv |
.\data\sales.csv |
相对路径 | 与 data/sales.csv 等价 |
相对路径更便于把整个项目复制到另一台电脑,但必须统一工作目录;绝对路径定位明确, 却会把盘符和用户名写死,不适合提交给他人。
四、Windows 路径字符串的正确写法¶
反斜杠在 Python 字符串中可能是转义符。例如 \n 表示换行,\t 表示制表符。
推荐优先使用 pathlib.Path:
from pathlib import Path
data_file = Path("D:/Python暑期训练/路径演示/data/sales.csv")
也可以使用原始字符串:
data_file = Path(r"D:\Python暑期训练\路径演示\data\sales.csv")
组合路径不要手工反复拼接斜杠:
project_root = Path("D:/Python暑期训练/路径演示")
data_file = project_root / "data" / "sales.csv"
Path 会根据操作系统生成合适的路径,代码也更容易阅读。
from pathlib import Path
relative_file = Path("data") / "sales.csv"
print("代码中写的相对路径:", relative_file)
print("按当前 cwd 解析后:", relative_file.resolve())
print("文件是否存在:", relative_file.exists())
五、直接运行 .py 文件时路径怎样变化¶
假设 main.py 位于:
D:\Python暑期训练\路径演示\scripts\main.py
情况 A:先进入项目根目录再运行¶
cd "D:\Python暑期训练\路径演示"
py scripts\main.py
此时通常:
cwd D:\Python暑期训练\路径演示
__file__ D:\Python暑期训练\路径演示\scripts\main.py
Path("data/sales.csv") D:\Python暑期训练\路径演示\data\sales.csv
sys.path[0] D:\Python暑期训练\路径演示\scripts
情况 B:先进入 scripts 再运行¶
cd "D:\Python暑期训练\路径演示\scripts"
py main.py
此时 cwd 变成 scripts,所以 Path("data/sales.csv") 会指向
scripts/data/sales.csv,通常会报 FileNotFoundError。
情况 C:在其他目录使用完整路径运行¶
cd "C:\Users\student"
py "D:\Python暑期训练\路径演示\scripts\main.py"
Python 找到了脚本,但 cwd 仍然是 C:\Users\student。因此脚本路径正确,
并不代表脚本中的相对数据路径也正确。
六、VS Code 运行 .py 时的工作目录¶
点击 Run Python File in Terminal 时:
- 脚本文件位置由当前编辑器标签决定;
cwd可能是打开的工作区根目录,也可能继承已经打开的终端目录;- VS Code 设置和终端之前执行过的
cd都可能影响结果。
因此程序开头可暂时加入:
from pathlib import Path
import sys
print("cwd =", Path.cwd())
print("__file__ =", Path(__file__).resolve())
print("python =", sys.executable)
使用 VS Code 时应当通过“文件 → 打开文件夹”打开整个项目根目录,不要只双击打开一个孤立的
.py 文件。运行前查看终端提示符,确认它当前位于哪里。
七、Jupyter Notebook 中的工作目录¶
Jupyter 内核的 cwd 通常是 Notebook 所在目录或内核启动目录;Classic Notebook、
JupyterLab、VS Code Notebook 和不同设置可能表现不同。唯一可靠的方法是现场检查:
from pathlib import Path
print(Path.cwd())
也可以使用 Jupyter 魔法命令:
%pwd
%cd ..
需要特别注意:
- Notebook 通常没有
__file__,不能直接复制脚本中的Path(__file__)写法; %cd会改变当前内核后续所有单元格的 cwd;!dir可以查看目录;!cd ..在临时子进程中执行,通常不会持续改变 Python 内核的 cwd;- 单元格执行顺序会保留状态,前面执行过的
%cd会影响后面读取文件。
假设 analysis.ipynb 的 cwd 为 路径演示/notebooks:
# 错误目标:notebooks/data/sales.csv
pd.read_csv("data/sales.csv")
# 正确目标:项目根目录下的 data/sales.csv
pd.read_csv("../data/sales.csv")
但 .. 是否正确仍取决于实际 cwd,所以读取前应先检查解析后的完整路径。
八、读取文件前的标准检查¶
以 pandas 读取 CSV 为例:
from pathlib import Path
import pandas as pd
data_file = Path("data") / "sales.csv"
print("cwd:", Path.cwd())
print("准备读取:", data_file.resolve())
print("是否存在:", data_file.exists())
if not data_file.exists():
raise FileNotFoundError(f"找不到文件:{data_file.resolve()}")
df = pd.read_csv(data_file, encoding="utf-8-sig")
print(df.head())
同一规则适用于:
pd.read_excel("data/orders.xlsx")
open("config.json", encoding="utf-8")
Path("notes.txt").read_text(encoding="utf-8")
写文件也从 cwd 解析。输出目录不存在时要先创建:
output_dir = Path("outputs")
output_dir.mkdir(parents=True, exist_ok=True)
df.to_csv(output_dir / "result.csv", index=False, encoding="utf-8-sig")
九、让 .py 文件稳定定位项目数据¶
如果脚本必须在任意目录都能运行,可以基于脚本自己的位置定位项目根目录。
假设脚本位于项目的 scripts 子目录:
from pathlib import Path
SCRIPT_FILE = Path(__file__).resolve()
SCRIPT_DIR = SCRIPT_FILE.parent
PROJECT_ROOT = SCRIPT_DIR.parent
DATA_FILE = PROJECT_ROOT / "data" / "sales.csv"
OUTPUT_DIR = PROJECT_ROOT / "outputs"
print("脚本:", SCRIPT_FILE)
print("项目根目录:", PROJECT_ROOT)
print("数据文件:", DATA_FILE)
| 表达式 | 结果 |
|---|---|
Path(__file__) |
当前 .py 文件路径 |
.resolve() |
转成规范化的绝对路径 |
.parent |
上一级目录 |
.parents[1] |
上两级目录 |
在被导入的库文件中,__file__ 指向的是该库文件自身,不是调用它的主程序。
这适合定位库自带的模板、配置等资源,但不应假定它等于用户的数据目录。
十、Notebook 中稳定定位项目根目录¶
Notebook 没有可靠的 __file__,通常先以 Path.cwd() 为起点。若 Notebook 固定放在
notebooks 子目录,可明确写出:
from pathlib import Path
NOTEBOOK_DIR = Path.cwd().resolve()
PROJECT_ROOT = NOTEBOOK_DIR.parent
DATA_FILE = PROJECT_ROOT / "data" / "sales.csv"
print("Notebook cwd:", NOTEBOOK_DIR)
print("项目根目录:", PROJECT_ROOT)
print("数据文件:", DATA_FILE)
更稳妥的项目规范是:
- 固定
data、notebooks、scripts、src、outputs的层级; - Notebook 开头只定义一次
PROJECT_ROOT; - 后面所有文件路径都由
PROJECT_ROOT / ...组合; - 不在多个单元格中随意使用
%cd或os.chdir()。
如果项目目录层级变化,只需修改根目录定义,不需要逐行修改所有读取代码。
十一、数据文件路径与模块引用路径不是一回事¶
假设 scripts/main.py 中包含:
from src.cleaner import clean_sales
df = pd.read_csv("data/sales.csv")
两行代码使用两套机制:
| 代码 | 搜索依据 |
|---|---|
pd.read_csv("data/sales.csv") |
cwd |
from src.cleaner import ... |
sys.path |
查看模块搜索路径:
import sys
for item in sys.path:
print(item)
查看一个已经导入的库来自哪里:
import pandas
print(pandas.__file__)
site-packages 中的第三方库通常可以从任何项目导入;自己写的 src/cleaner.py 只有在项目根目录
位于 sys.path 中,或项目已经被安装成软件包时才能稳定导入。
十二、直接运行脚本与 python -m 的区别¶
在项目根目录执行:
py scripts\main.py
Python 把 scripts 目录放在模块搜索路径前部,main.py 被当作独立脚本运行。
此时包内相对导入可能报:
ImportError: attempted relative import with no known parent package
如果 scripts 是带 __init__.py 的包,可以在项目根目录使用:
py -m scripts.main
-m 表示按模块运行:
- 当前工作目录仍是项目根目录;
- 项目根目录进入模块搜索路径;
- Python 知道
main属于scripts包; from .helper import ...这类包内相对导入可以正常解析。
from .helper import x 中的点表示“相对于当前 Python 包”,不是相对于 cwd,也不是普通文件路径的 .。
十三、在 Jupyter 中引用本地 .py 模块¶
如果 Notebook 的 cwd 是 notebooks,而模块位于项目根目录的 src 中,直接
from src.cleaner import ... 可能失败,因为项目根目录不在 sys.path。
初学阶段可明确加入项目根目录:
from pathlib import Path
import sys
PROJECT_ROOT = Path.cwd().resolve().parent
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
from src.cleaner import clean_sales
这是一种过渡性做法。正式项目更推荐把本地代码组织成包,再使用
py -m pip install -e . 进行可编辑安装。
在 Notebook 中运行脚本还可使用:
%run ../scripts/main.py
%run 找到了脚本,不代表 cwd 自动切换到脚本目录;脚本中的相对数据路径仍可能按 Notebook
当前 cwd 解析。
十四、典型路径错误与诊断顺序¶
| 报错或现象 | 重点检查 |
|---|---|
FileNotFoundError |
Path.cwd()、目标路径 .resolve()、.exists() |
| 读取了旧文件 | 实际解析路径、同名文件、终端所在目录 |
| 输出文件找不到 | cwd、输出目录是否提前创建 |
No module named src |
sys.path 是否包含项目根目录 |
| 包内相对导入失败 | 是否直接运行了包内文件,是否应改用 py -m |
| Notebook 能运行、脚本不能 | Notebook 曾修改 cwd/sys.path 或保留了旧变量 |
| 脚本能运行、Notebook 不能 | Notebook cwd、内核解释器、项目根目录是否在 sys.path |
路径报错时固定打印:
from pathlib import Path
import sys
print("cwd =", Path.cwd())
print("python =", sys.executable)
print("sys.path[0] =", sys.path[0])
print("target =", Path("data/sales.csv").resolve())
print("exists =", Path("data/sales.csv").exists())
在 .py 中再增加 print("__file__ =", Path(__file__).resolve())。
十五、Python 与 pip 版本检查¶
Windows 常用命令:
py --version
py -m pip --version
如果电脑不支持 py,使用:
python --version
python -m pip --version
python -m pip 的含义是:让当前这个 Python 启动它自己的 pip。这样能减少“包装在 A 环境,代码却由 B 环境运行”的问题。
十六、第一阶段需要的软件包¶
py -m pip install pandas openpyxl notebook ipykernel
| 软件包 | 用途 |
|---|---|
pandas |
表格数据清洗、计算、分组和导出 |
openpyxl |
支持 pandas 读写 .xlsx |
notebook、ipykernel |
运行 Notebook 课件和 Python 内核 |
网页采集阶段还会使用:
py -m pip install requests beautifulsoup4 lxml
在 Jupyter Notebook 中需要临时安装包时优先使用:
%pip install pandas
%pip 会针对当前 Notebook 内核安装。安装完成后如仍无法导入,重启内核再运行。
十七、安装、导入与文件路径的区别¶
pip install pandas
是把 pandas 安装进某个 Python 环境。
import pandas
是在当前程序中使用已经安装的 pandas。
安装通常只需做一次;每个需要使用 pandas 的程序都要写 import。
三者不要混淆:
| 操作 | 主要依据 |
|---|---|
py -m pip install pandas |
py 指向的 Python 环境 |
import pandas |
当前解释器的 sys.path |
pd.read_csv("data/a.csv") |
当前进程的 cwd |
十八、软件包及安装位置检查¶
from importlib import import_module
import sys
print("当前内核 Python:", sys.executable)
for package_name in ["pandas", "openpyxl"]:
try:
package = import_module(package_name)
print(f"{package_name} 版本:", package.__version__)
print(f"{package_name} 路径:", package.__file__)
except ModuleNotFoundError:
print(f"{package_name} 未安装在当前内核中")
代码解读¶
| 代码 | 含义 |
|---|---|
sys.executable |
查看当前 Notebook 内核的 Python 路径 |
import_module(package_name) |
根据字符串动态导入指定模块 |
package.__version__ |
读取当前导入模块的版本字符串 |
package.__file__ |
查看当前导入模块的实际文件位置 |
except ModuleNotFoundError |
缺包时输出诊断信息,避免整份课件中断 |
常见别名写法:
import pandas as pd
后续即可用 pd.DataFrame(),这是三套真题中的标准写法。
十九、环境与路径联合排错¶
| 报错或现象 | 常见原因 | 检查顺序 |
|---|---|---|
No module named pandas |
当前解释器没有安装 pandas | 看 VS Code 解释器,再用该 Python 安装 |
python 不是内部或外部命令 |
PATH 或命令名问题 | 尝试 py,重开终端 |
Permission denied |
账号没有安装权限 | 保存完整报错,使用管理员或用户安装 |
| 下载超时 | 网络或镜像问题 | 重试一次,再使用离线包 |
| Notebook 内核与终端不同 | 选了不同环境 | 检查 Notebook 右上角内核 |
排错时应记录:运行命令、cwd、脚本路径、目标文件解析路径、Python 路径、pip 路径和完整报错最后一行。
二十、与真题的关系¶
- 名仕题:
requests、json、pandas、openpyxl - 科图题:
requests、beautifulsoup4、pandas、lxml - 青娅题:
requests、beautifulsoup4、pandas、lxml
三套真题既会导入第三方库,也会读取页面数据、保存 CSV/Excel。Excel 导出失败时,
不一定是 to_excel() 写错,也可能是缺少 openpyxl、输出目录不存在或 cwd 与预期不同。
针对性练习¶
使用三个不同目录运行 学生练习.py,比较 cwd、__file__、sys.path[0]、
相对路径和脚本锚定路径。然后把诊断代码复制到 Notebook,观察 __file__ 和工作目录的变化。