第 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 当前使用哪个环境、去哪个环境找第三方包

这四个位置可能相同,也可能完全不同。最关键的两条规则是:

  1. open("data/a.csv")、pd.read_csv("data/a.csv") 等相对文件路径,默认从 cwd 开始;
  2. 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)

更稳妥的项目规范是:

  1. 固定 data、notebooks、scripts、src、outputs 的层级;
  2. Notebook 开头只定义一次 PROJECT_ROOT;
  3. 后面所有文件路径都由 PROJECT_ROOT / ... 组合;
  4. 不在多个单元格中随意使用 %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__ 和工作目录的变化。

2026 暑期 Python 零基础训练|Notebook 课件 HTML 版