第01篇|环境搭建:Python、uv/conda、VS Code、Jupyter(新版)
本篇定位:《Python 教程》专栏一「Python 从零入门」的第 1 篇,也是整个 48 篇系列的地基。
读完你将能够:说清楚“Python 解释器、虚拟环境、包”三者是什么关系;用 uv(或 conda)为项目创建干净的环境;在 VS Code 和 Jupyter 里选对解释器;跑通第一个 Hello World,并用 NumPy / PyTorch 做一次安装自检。
预计用时:阅读 40 分钟,动手 30~60 分钟(主要花在下载上)。
很多教程的第一篇只有一句话:“去官网下载 Python,一路下一步。”这样确实能装上,但几周之后你大概率会遇到这些问题:pip install 明明成功了,import 却报 ModuleNotFoundError;电脑里不知不觉装了四五个 Python;装 PyTorch 时 CUDA 版本对不上;Jupyter 里能用的包在终端里用不了……
这些问题的根源都一样:不知道自己正在用的是哪一个 Python,也不知道包装到了哪里。所以本篇不只教你“怎么装”,还要讲清楚“装的是什么、装到了哪、程序运行时是怎么找到它们的”。这些知识在后面的第 09 篇(import 机制)、第 20 篇(依赖管理与打包)、第 38 篇(GPU 与 CUDA)会反复用到,对以后部署 vLLM / SGLang 这类推理框架更是基本功。
一、先搞懂概念:Python 到底是什么?
1.1 “Python”其实指三样东西
日常说“Python”,可能指三种不同的东西,初学者一定要分开:
- Python 语言:一套语法规范,规定
for、def、class怎么写、是什么意思。它只是“文档”,本身不能运行。 - Python 解释器:一个真正能运行的程序(在 macOS/Linux 上通常叫
python3,在 Windows 上叫python.exe),它读取你写的.py文件并执行。我们说“安装 Python”,装的就是它。 - Python 生态:标准库(随解释器自带的
os、json、math等模块)加上 PyPI 上数十万个第三方包(NumPy、PyTorch、FastAPI……)。
解释器有多个实现:官方的 CPython(用 C 语言编写,占绝对主流)、带 JIT 的 PyPy、跑在 JVM 上的 Jython 等。本系列说的“Python 解释器”默认都是 CPython。AI 领域几乎只用 CPython,因为 NumPy、PyTorch 这些库都是基于 CPython 的 C API 写的扩展模块。
1.2 一个 .py 文件是怎么跑起来的

图1 Python 程序执行流程
当你在终端输入 python hello.py 时,CPython 大致做了四件事:
- 读取源码:把
hello.py当作文本读进来。 - 词法/语法分析:把文本切分成一个个“词”(token),再按语法规则组装成一棵抽象语法树(AST)。语法错误(
SyntaxError)就是在这一步发现的,所以一个文件里只要有一处语法错误,整个文件一行都不会执行。 - 编译成字节码:把 AST 翻译成一种面向虚拟机的中间指令,叫字节码(bytecode)。被
import的模块,其字节码会缓存到pycache/xxx.cpython-312.pyc,下次导入时如果源码没变就直接复用,省去编译时间。 - 虚拟机执行:CPython 内部的虚拟机(PVM)是一个大循环,逐条取出字节码指令并执行。
你可以亲眼看看字节码长什么样:
import dis
def add(a, b):
return a + b
dis.dis(add)
输出类似:
3 0 RESUME 0
4 2 LOAD_FAST 0 (a)
4 LOAD_FAST 1 (b)
6 BINARY_OP 0 (+)
10 RETURN_VALUE
(以上是 Python 3.12 的输出;3.13 起会把两条 LOAD_FAST 合并成一条 LOAD_FAST_LOAD_FAST,这正说明字节码是解释器的内部实现细节,不同版本可以不同。)
这里有一个对 AI Infra 非常重要的结论:字节码由虚拟机逐条解释执行,每条指令都要做类型检查、引用计数等额外工作,所以纯 Python 的数值循环非常慢(比 C 慢几十到上百倍)。NumPy 和 PyTorch 的做法是:Python 只负责“描述要做什么”,真正的矩阵运算交给 C/C++/CUDA 写好的底层代码批量完成。理解这一点,你就明白为什么后面第 32 篇要专门讲“向量化”,以及为什么推理框架会把热点路径全部下沉到 CUDA kernel。
1.3 PATH:终端是怎么找到 python 的
你在终端输入 python,系统并不知道它在哪。操作系统会读取一个叫 PATH 的环境变量,它是一串用冒号(Windows 上是分号)分隔的目录列表,系统从左到右依次在这些目录里找名为 python 的可执行文件,找到第一个就停止。
# macOS / Linux
echo $PATH
which -a python3 # 列出 PATH 中所有叫 python3 的程序,第一个就是实际使用的
# Windows PowerShell
$env:Path -split ';'
where.exe python
这就解释了“电脑里装了多个 Python”时的混乱:你运行的是哪个,完全取决于 PATH 的顺序。后面讲到的“激活虚拟环境”,本质上就是把虚拟环境的目录临时插到 PATH 最前面。
1.4 包装到哪里去了:site-packages 与 sys.path
当你执行 pip install numpy,pip 做了这样几件事:
- 去包索引(默认是 PyPI,pypi.org)查询 numpy 有哪些版本,结合你的 Python 版本、操作系统、CPU 架构挑一个合适的;
- 下载一个 wheel 文件(
.whl,本质是 zip 压缩包,里面是已经编译好的代码,例如numpy-2.x.y-cp312-cp312-manylinux_2_17_x86_64.whl,文件名里的cp312表示适用于 CPython 3.12);如果没有匹配的 wheel,就只能下载源码包(sdist)在本地编译——这通常就是“安装半天然后报一堆 C 编译错误”的原因; - 把 wheel 解压到当前解释器的
site-packages目录,同时写入一个numpy-2.x.y.dist-info目录记录版本和文件清单(pip list、pip uninstall就靠它)。
而 import numpy 时,解释器会按 sys.path 这个列表里的目录顺序去找名为 numpy 的模块或包。site-packages 就在这个列表里。你可以自己验证:
import sys
print(sys.executable) # 当前正在运行的解释器的完整路径
print(sys.prefix) # 当前环境的根目录
for p in sys.path:
print(p)
关键结论:每一个解释器(每一个环境)都有自己独立的 site-packages。用 A 解释器的 pip 装的包,B 解释器看不到。这是本篇最重要的一句话,后面几乎所有的“踩坑”都能用它解释。
1.5 为什么需要虚拟环境
设想你有两个项目:一个老项目依赖 numpy 1.26,一个新的推理项目需要 numpy 2.x 和特定版本的 torch。如果都往同一个 site-packages 里装,后装的会覆盖先装的,总有一个项目跑不起来。
虚拟环境(virtual environment)就是为了解决这个问题:为每个项目准备一个独立的 site-packages,彼此隔离。

图2 解释器、虚拟环境与包的关系
注意图中左侧的“系统自带 Python”:macOS 和大多数 Linux 发行版都自带一个 Python,系统的一些工具依赖它。永远不要用 sudo pip install 往系统 Python 里装包,轻则版本冲突,重则把 yum、apt 之类的系统工具搞坏。新版 Ubuntu/Debian 甚至会直接拒绝(报 externally-managed-environment,下文踩坑部分细说)。
那么一个虚拟环境里到底有什么?用标准库自带的 venv 创建一个看看:
mkdir my-project && cd my-project
python3 -m venv .venv # 在当前目录创建名为 .venv 的虚拟环境
ls .venv # Windows: dir .venv
cat .venv/pyvenv.cfg

图3 venv 目录结构
你会发现虚拟环境非常“轻”:它并没有复制一整套 Python,bin/python 只是指向基础解释器的链接(Windows 上是一个小的启动器),pyvenv.cfg 里用 home = ... 记录基础解释器的位置。Python 启动时如果发现自己旁边(或上一级目录)有 pyvenv.cfg,就会把 sys.prefix 设为这个环境目录,从而使用环境自己的 site-packages。
激活与退出:
# macOS / Linux
source .venv/bin/activate
# Windows PowerShell
.venv\Scripts\Activate.ps1
# Windows cmd
.venv\Scripts\activate.bat
which python # 激活后应指向 .../my-project/.venv/bin/python
deactivate # 退出虚拟环境
activate 脚本做的事情很简单:把 .venv/bin 放到 PATH 最前面,再改一下命令行提示符(出现 (.venv) 前缀)。所以不激活也完全可以用:直接运行 .venv/bin/python script.py 效果一样。理解这一点,你就不会把“激活”当成什么神秘操作了。
二、工具选择:venv、uv、conda 怎么选
理解了原理,再看工具就清楚了:它们都在做同一件事——管理解释器、创建隔离环境、往环境里装包,区别在于速度、范围和适用场景。
| 维度 | venv + pip | uv | conda / mamba(Miniforge) |
|---|---|---|---|
| 来源 | Python 自带 | Astral 公司,Rust 编写 | Anaconda / conda-forge 社区 |
| 安装 Python 本身 | 不能,需先有 Python | 能(uv python install) | 能 |
| 速度 | 一般 | 极快(常见 10~100 倍) | 较慢(mamba 较快) |
| 包来源 | PyPI | PyPI(兼容 pip) | conda 频道,也可混用 pip |
| 非 Python 依赖(CUDA、MKL、编译器) | 不能 | 不能 | 能 |
| 锁文件 / 可复现 | 需手动 pip freeze | 自动 uv.lock | environment.yml,锁需额外工具 |
| 环境位置 | 项目内 .venv | 项目内 .venv | 集中在 ~/miniforge3/envs/ |

图4 环境管理工具选择决策图
本系列的建议:
- 默认用 uv。它一个工具就能完成“装 Python + 建环境 + 装包 + 锁版本 + 运行”,速度快到让你忘记在等待,而且它创建的就是标准的
.venv,和上一节讲的原理完全一致。近两年 AI 开源项目(包括 vLLM、SGLang 的官方文档)也越来越多地推荐用 uv 安装。 - conda 什么时候用?当你需要 Python 之外的二进制依赖时,比如要在没有 root 权限的共享 GPU 服务器上装特定版本的 CUDA toolkit(含
nvcc编译器)、cuDNN、gcc,或者公司/实验室的老项目已经基于 conda。推荐安装 Miniforge(默认使用社区的 conda-forge 频道,没有 Anaconda 商业授权的顾虑)。 - venv + pip 必须懂:它是“最低公约数”,任何有 Python 的机器上都能用,uv 和 conda 的很多行为也是在它的基础上设计的。
为什么做 AI Infra 的人总在折腾 conda/uv?
PyTorch 这类库的 wheel 里包含了大量 C++/CUDA 编译产物,一个 CUDA 版的 torch wheel 动辄数 GB,而且和 CUDA 版本(cu118 / cu121 / cu124 / cu128 …)、Python 版本、驱动版本强绑定。vLLM、SGLang 又会进一步锁定特定的 torch 版本。一台服务器上同时维护“vLLM 环境”“SGLang 环境”“训练环境”是常态,如果不用环境隔离,几乎一定会互相打架。好消息是:PyTorch 官方 wheel 已经自带了 CUDA 运行时库,只要显卡驱动足够新,用 uv/pip 直接装就能用 GPU;只有需要用
nvcc从源码编译自定义算子(比如编译 flash-attention 或推理框架的内核)时,才需要完整的 CUDA toolkit,这时 conda 或系统级 CUDA 才派上用场。第 38 篇会详细讲 CUDA 的层次关系。
关于 Python 版本:本系列统一使用 Python 3.12。它足够新(性能、报错提示都有明显改进),同时几乎所有 AI 相关库都已提供 3.12 的 wheel。更新的版本当然也能学语法,但 PyTorch 及其周边生态对最新版本的支持通常会滞后一段时间,做 AI 项目时“新”不一定是好事。
三、动手安装:用 uv 搭建第一个项目
下面的步骤在 macOS、Linux、Windows 上都适用,差异处会单独注明。请打开终端(macOS:“终端”App;Windows:Windows Terminal 或 PowerShell;Linux:你熟悉的终端)。
3.1 安装 uv
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows PowerShell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
# 也可以:macOS 用 brew install uv;已有 Python 时用 pip install uv
安装脚本会把 uv 放到 ~/.local/bin(Windows 为 %USERPROFILE%\.local\bin)并尝试加入 PATH。安装完请关闭并重新打开终端,让新的 PATH 生效,然后验证:
uv --version
国内网络提示:如果 PyPI 下载很慢,可以设置镜像。uv 使用环境变量
UV_DEFAULT_INDEX(写进~/.bashrc/~/.zshrc可永久生效):
export UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simplepip 对应的写法是
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple。uv 下载 Python 解释器本身走的是 GitHub,若很慢可以用
UV_PYTHON_INSTALL_MIRROR指定镜像,或先用系统/官网安装的 Python。
3.2 安装 Python 解释器
uv python install 3.12 # 下载一个独立的 CPython 3.12
uv python list # 查看 uv 能找到的所有 Python(包括系统自带的)
uv 安装的 Python 放在自己的目录下,不会覆盖系统 Python,也不会弄乱 PATH,这正是我们想要的。
3.3 创建项目
uv init --no-package hello-infra --python 3.12
cd hello-infra
ls -a
--no-package 表示这是一个“直接运行脚本”的普通应用项目,而不是要打包发布的库(不同版本的 uv 默认行为略有差异,显式写上最稳妥;打包相关内容留到第 20 篇)。你会看到这些文件(另外还有一个 .git 目录,uv 顺手帮你初始化了 Git 仓库):
hello-infra/
├── .python-version # 记录本项目使用的 Python 版本(3.12)
├── .gitignore # 已经写好忽略 .venv 等
├── README.md
├── main.py # 示例入口
└── pyproject.toml # 项目元信息与依赖清单(现代 Python 项目的标准配置文件)
打开 pyproject.toml:
[project]
name = "hello-infra"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
dependencies 目前是空的。第 20 篇会系统讲 pyproject.toml,现在只需要知道:它是项目依赖的“唯一真相来源”。
3.4 添加依赖并运行
uv add numpy # 添加依赖:写入 pyproject.toml,更新 uv.lock,并安装到 .venv
uv run main.py # 用本项目的 .venv 运行 main.py(首次运行会自动创建 .venv)
uv add 背后做了三件事:把 numpy 写进 dependencies;解析出所有依赖的精确版本并写入 uv.lock;把包安装进项目下的 .venv。uv run 则保证命令一定在这个项目的环境中执行——你甚至不需要手动激活环境,这就从根源上避免了“用错 Python”。
几个常用命令先混个脸熟:
uv add "torch>=2.4" # 添加带版本约束的依赖
uv add --dev ipykernel pytest # 添加开发依赖(只在开发时需要)
uv remove numpy # 移除依赖
uv sync # 按 uv.lock 把 .venv 同步到精确一致的状态(拿到别人项目后第一件事)
uv pip list # 查看 .venv 里装了哪些包
uv run python # 打开本项目环境的交互式解释器
如果你更习惯传统的激活方式,也完全可以:
source .venv/bin/activate # Windows: .venv\Scripts\activate
python main.py
3.5 备选方案:用 Miniforge(conda)
如果你决定用 conda(例如在 GPU 服务器上),流程如下:
# Linux / macOS:下载并安装 Miniforge
curl -L -O "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
bash Miniforge3-$(uname)-$(uname -m).sh # 按提示操作,最后选择 yes 初始化 shell
# Windows:到 github.com/conda-forge/miniforge 下载 .exe 安装包
# 重新打开终端后
conda create -n infra python=3.12 -y # 创建名为 infra 的环境
conda activate infra # 激活
python -c "import sys; print(sys.executable)" # 应指向 .../miniforge3/envs/infra/bin/python
conda install numpy -y # 从 conda-forge 安装
pip install requests # 在 conda 环境里用 pip 也可以(装进同一个环境)
conda deactivate
conda env list # 查看所有环境
conda 的环境不在项目目录里,而是集中放在 miniforge3/envs/ 下,这方便多个项目共用一个大环境(比如一个装好 torch 的环境),但也更容易让“哪个项目用哪个环境”变得模糊。混用规则:在 conda 环境里,先用 conda 装好 Python 和二进制依赖,再用 pip(或 uv pip)装纯 Python 包;不要反复交替使用两者去升级同一个包。
3.6 最后的保底:venv + pip
在一台只有 Python、不能装任何额外工具的机器上:
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
python -m pip install --upgrade pip
python -m pip install numpy
python -m pip freeze > requirements.txt # 导出依赖清单
注意这里用的是 python -m pip 而不是直接 pip:前者明确使用当前这个 python 对应的 pip,而单独敲 pip 可能会被 PATH 解析到另一个 Python 的 pip 上。这是一个值得养成的好习惯。
四、VS Code:写代码的主力编辑器
4.1 安装与插件
到 code.visualstudio.com 下载安装 VS Code。
打开左侧“扩展”面板(
Ctrl+Shift+X,macOS 为Cmd+Shift+X),安装:- Python(Microsoft 出品,会连带安装 Pylance 语言服务器与 Python Debugger);
- Jupyter(Microsoft 出品,在 VS Code 里直接运行 Notebook);
- 可选:Ruff(极快的代码检查与格式化工具,与 uv 同一家公司)。
命令行快速打开项目:在项目目录执行
code .(macOS 需先在 VS Code 中执行命令面板里的 “Shell Command: Install ‘code’ command in PATH”)。
4.2 选择解释器:最关键的一步

图5 终端、VS Code 与 Jupyter 如何确定使用哪个 Python
VS Code 本身不带 Python,它需要知道用哪一个解释器来运行代码、做代码补全和类型检查。操作方法:
- 按
Ctrl+Shift+P(macOS:Cmd+Shift+P)打开命令面板; - 输入并选择 Python: Select Interpreter;
- 在列表中选择项目下的
.venv(显示为类似Python 3.12.x ('.venv': venv) ./.venv/bin/python)。如果用的是 conda,选择infra环境。
选好之后,窗口右下角状态栏会显示当前解释器。VS Code 打开项目文件夹时,通常会自动识别项目内的 .venv;如果列表里找不到,可以选 “Enter interpreter path…” 手动指定路径。
判断是否选对的简单方法:如果 import numpy 下面出现黄色波浪线并提示 “Import”numpy” could not be resolved”,而你确定已经装过 numpy,那九成是解释器选错了。
4.3 运行与调试
- 点击右上角的 ▶ 按钮(Run Python File),VS Code 会在内置终端里用你选中的解释器运行当前文件;
- 在行号左侧点一下设置断点,按
F5选择 “Python Debugger: Current File” 即可单步调试,查看变量值。调试是比print高效得多的排错方式,第 21 篇会深入讲。 - VS Code 新打开的内置终端会自动激活所选的虚拟环境(终端提示符前出现
(.venv)或(hello-infra))。
五、Jupyter:交互式实验的利器
5.1 Notebook 与 Kernel
Jupyter Notebook(.ipynb 文件)把代码、输出、图表和说明文字放在一个文档里,按“单元格(cell)”执行,非常适合做数据探索和模型实验。后面 NumPy、PyTorch 两个专栏的大量示例都推荐在 Notebook 里边跑边看。
理解 Jupyter 的关键是区分前端和内核(kernel)(见上面的图 5):
- 前端:你看到的界面,可以是浏览器里的 JupyterLab,也可以是 VS Code;
- 内核:一个在后台运行的独立 Python 进程,由某个环境里的
python加上ipykernel包启动。你在单元格里写的代码被发给内核执行,变量都活在内核进程的内存里。
所以,Notebook 能用哪些包,取决于内核是哪个环境的 Python,而与你从哪里启动 Jupyter 无关。这也解释了为什么“重启内核”会清空所有变量——那是把后台进程杀掉重开。
5.2 在 VS Code 中使用 Notebook(推荐)
cd hello-infra
uv add --dev ipykernel # 让本项目的环境能充当 Jupyter 内核
然后在 VS Code 中新建文件 explore.ipynb,点击右上角的 Select Kernel → Python Environments → 选择 .venv。在第一个单元格输入:
import sys
sys.executable
按 Shift+Enter 运行,输出应当是你项目 .venv 里的 python 路径。
5.3 使用 JupyterLab(浏览器)
# 方式一:临时带上 jupyterlab 运行,不写入项目依赖
uv run --with jupyterlab jupyter lab
# 方式二:把本项目环境注册为一个全局可见的内核,供任何 Jupyter 使用
uv run python -m ipykernel install --user --name hello-infra --display-name "Python (hello-infra)"
jupyter kernelspec list # 查看已注册的内核及其位置
注册内核本质上是在用户目录下写一个 kernel.json,里面记录了“用哪个 python 可执行文件启动内核”。打开看看就会明白:内核就是一个指向特定解释器的“快捷方式”。
在 Notebook 里临时装包时,请用魔法命令 %pip install xxx(而不是 !pip install xxx),%pip 会保证装到当前内核所在的环境中。不过在 uv 项目中更推荐回到终端用 uv add,让依赖记录在 pyproject.toml 里。
六、第一个程序与安装自检
6.1 Hello World
把 main.py 改成下面的内容:
import platform
import sys
def main() -> None:
print("Hello, AI Infra!")
print(f"Python 版本 : {platform.python_version()}")
print(f"实现 : {platform.python_implementation()}") # 应为 CPython
print(f"解释器路径 : {sys.executable}")
print(f"环境根目录 : {sys.prefix}")
print(f"是否在虚拟环境中: {sys.prefix != sys.base_prefix}")
if __name__ == "__main__":
main()
运行:
uv run main.py
预期输出(路径因人而异):
Hello, AI Infra!
Python 版本 : 3.12.x
实现 : CPython
解释器路径 : /Users/you/hello-infra/.venv/bin/python
环境根目录 : /Users/you/hello-infra/.venv
是否在虚拟环境中: True
sys.prefix != sys.base_prefix 是判断是否处于虚拟环境的标准方法:base_prefix 指向基础解释器,prefix 指向当前环境。if name == "main": 这一行的含义留到第 09 篇讲模块时再展开,现在只需知道“直接运行这个文件时才执行 main()”。
6.2 NumPy 自检
# check_numpy.py
import numpy as np
print("numpy 版本:", np.__version__)
print("安装位置 :", np.__file__) # 应位于 .venv/.../site-packages/numpy/ 下
a = np.arange(6).reshape(2, 3)
b = np.ones((3, 2))
print("a @ b =\n", a @ b) # 矩阵乘法,结果应为 [[3,3],[12,12]]
uv run check_numpy.py
留意 np.file 打印的路径:它正是 1.4 节说的 site-packages。以后遇到“到底用的是哪个版本的包”,打印 包名.file 和 包名.version 是最直接的排查手段。
6.3 PyTorch 自检(可选,但强烈建议做)
PyTorch 较大,先根据你的机器选择安装方式:
# 有 NVIDIA 显卡的 Linux / Windows:直接安装,默认会带上 CUDA 版本(下载量数 GB)
uv add torch
# 没有 NVIDIA 显卡、只想学习语法(包括 Mac):
# macOS 直接 uv add torch 即可(自带 Apple GPU 的 MPS 支持)
# Linux 只想要 CPU 版以节省空间,可在环境里执行:
uv pip install torch --index-url https://download.pytorch.org/whl/cpu
说明:
uv add会把依赖写入项目配置;uv pip install只装进当前.venv而不记录,适合临时试用。在项目中固定使用 CPU 版或特定 CUDA 版的正规做法是在pyproject.toml中配置 PyTorch 的索引,我们在第 20 篇和第 38 篇再细讲。具体的安装命令请以 pytorch.org 首页的 “Get Started” 选择器为准。
# check_torch.py
import torch
print("torch 版本 :", torch.__version__)
print("编译所用 CUDA :", torch.version.cuda) # CPU 版为 None
print("CUDA 可用 :", torch.cuda.is_available())
if torch.cuda.is_available():
device = "cuda"
print("GPU 型号 :", torch.cuda.get_device_name(0))
elif getattr(torch.backends, "mps", None) and torch.backends.mps.is_available():
device = "mps" # Apple Silicon GPU
else:
device = "cpu"
print("使用设备 :", device)
x = torch.randn(1024, 1024, device=device)
y = x @ x.T
print("矩阵乘法结果形状:", tuple(y.shape), "设备:", y.device)
uv run check_torch.py
在 GPU 机器上,如果 CUDA 可用 为 False,按顺序排查:
- 终端执行
nvidia-smi,确认驱动正常,右上角显示的 “CUDA Version” 是驱动所支持的最高 CUDA 版本; torch.version.cuda是 torch 编译时使用的 CUDA 版本,它必须 ≤ 驱动支持的版本;- 如果
torch.version.cuda是None,说明装成了 CPU 版,需要重装对应的 CUDA 版本。
这三者的关系(驱动 / CUDA runtime / toolkit)是 AI Infra 的必修课,第 38 篇专门讲。
七、踩坑大全:按平台分类
7.1 通用问题
坑 1:pip install 成功了,import 却报 ModuleNotFoundError。 原因:pip 和 python 不是同一个环境的。排查三连:
which python; which pip # Windows: where python / where pip
python -m pip --version # 会打印这个 pip 属于哪个 Python
python -c "import sys; print(sys.executable)"
解决:统一使用 python -m pip install ...,或在 uv 项目中使用 uv add / uv run。
坑 2:VS Code 能运行,终端不能(或反过来)。 两者用的解释器不同。VS Code 看右下角状态栏,终端看 which python,让两者指向同一个 .venv。
坑 3:Jupyter 里 import torch 失败,终端里却正常。 Notebook 的内核选错了环境。在 Notebook 里运行 import sys; sys.executable 核对,并在右上角重新选择内核。
坑 4:把 .venv 提交到了 Git,或者把项目文件夹整个拷到另一台电脑后环境不能用。 虚拟环境里有指向本机基础解释器的绝对路径,不可移植。正确做法:只提交 pyproject.toml 和 uv.lock,在新机器上执行 uv sync 重建。.venv 可以随时删除重建,不要心疼。
坑 5:给自己的脚本起名 numpy.py、torch.py、random.py。 sys.path 的第一项是脚本所在目录,同名文件会“遮蔽”真正的库,报出奇怪的 AttributeError 或循环导入错误。脚本不要和常用库同名。
坑 6:路径中含有中文或空格。 大多数工具已经能处理,但一些老的编译工具链、CUDA 相关脚本仍可能出错。建议把代码放在纯英文、无空格的路径下,例如 ~/code/hello-infra。
7.2 macOS
- 系统里没有
python命令,只有python3:macOS 自带(或通过 Xcode 命令行工具提供)的是python3。激活虚拟环境或使用uv run后,python就可用了。首次使用可能弹窗要求安装“命令行开发者工具”,执行xcode-select --install即可。 - Homebrew 的 Python 报
externally-managed-environment:Homebrew 禁止直接往它的 Python 里 pip 装包。这正是在提醒你:去用虚拟环境。 - Apple Silicon(M 系列芯片):请确保终端和 Python 都是 arm64 原生而非 Rosetta 转译(
python -c "import platform; print(platform.machine())"应输出arm64),否则可能装到 x86 的包,性能差甚至报错。
7.3 Linux / GPU 服务器
error: externally-managed-environment:Ubuntu 23.04+、Debian 12+ 等发行版遵循 PEP 668,禁止 pip 修改系统 Python。不要加--break-system-packages硬装,而是使用 uv 或 venv 创建虚拟环境。- 没有 root 权限:uv 和 Miniforge 都装在用户目录下,完全不需要 sudo,这也是它们在共享服务器上受欢迎的原因。
- conda 的
(base)总是自动激活:可能让你误以为自己在某个环境里。可执行conda config --set auto_activate_base false关闭。 - conda 和 uv 同时存在:在已激活的 conda 环境里运行
uv pip install会装进这个 conda 环境;而uv run/uv sync使用的是项目的.venv。两套体系可以共存,但同一个项目请只选一种,并用sys.executable确认。 - 磁盘被缓存撑满:torch 等大包会在
~/.cache/uv、~/.cache/pip、~/miniforge3/pkgs留下缓存。可用uv cache clean、pip cache purge、conda clean -a清理;服务器 home 目录较小时,可设置UV_CACHE_DIR把缓存放到数据盘。
7.4 Windows
- 输入
python却弹出了 Microsoft Store:这是系统的“应用执行别名”。到“设置 → 应用 → 高级应用设置 → 应用执行别名”中关闭python.exe和python3.exe两项;或者直接使用 uv 管理的 Python。 - 官网安装包忘了勾选 “Add python.exe to PATH”:重新运行安装程序选择 Modify,或改用 uv。也可以用 Windows 自带的启动器
py -3.12指定版本。 - PowerShell 无法运行
Activate.ps1,提示“禁止运行脚本”:执行Set-ExecutionPolicy -Scope CurrentUser RemoteSigned后重试。 - 路径分隔符:Windows 用
\,虚拟环境的可执行文件在.venv\Scripts\而不是.venv/bin/。 - 想做 AI Infra 的同学:强烈建议安装 WSL2(Windows 里的 Linux 子系统),在其中按 Linux 流程操作。vLLM、SGLang 等推理框架主要面向 Linux,越早适应 Linux 环境越好。
八、小结
- “Python”是语言、解释器和生态的统称。我们安装的是 CPython 解释器:源码 → AST → 字节码 → 虚拟机执行。纯 Python 循环慢,所以数值计算要交给 NumPy/PyTorch 的底层实现。
- 终端通过 PATH 从左到右查找
python;import通过 sys.path 查找模块;pip 把包装进当前解释器的 site-packages。 - 虚拟环境 = 指向基础解释器的壳 + 独立的 site-packages;“激活”只是修改 PATH。
- 工具选择:默认 uv(快、可复现、一站式);需要 CUDA toolkit 等非 Python 依赖时用 conda(Miniforge);venv + pip 是必须理解的基础。
- VS Code 要通过 Python: Select Interpreter 选对解释器;Jupyter 的包取决于内核用的是哪个环境。
- 排查一切环境问题的万能钥匙:
sys.executable、python -m pip、包名.file。
九、课后练习
- (基础)用
uv init --no-package ex01创建项目,添加numpy和requests,运行 6.1 节的 Hello World,并截图输出。然后打开uv.lock,找到 numpy 的版本号和它对应的 wheel 文件名,说说文件名中cp312、manylinux/macosx/win_amd64分别代表什么。 - (理解 PATH)在终端依次执行:不激活时
which -a python3;激活.venv后echo $PATH和which python;deactivate后再看一次。用自己的话解释activate到底改变了什么。(Windows 用where python和$env:Path。) - (理解隔离)创建两个项目
proj_a、proj_b,分别安装numpy<2和numpy>=2,在两个项目中各运行一次python -c "import numpy; print(numpy.version, numpy.file)",观察版本和路径的差异。 - (手动造环境)不用 uv,用
python3 -m venv创建环境,不激活,直接用.venv/bin/python -m pip install rich(Windows:.venv\Scripts\python -m pip install rich)安装一个包,并用同一个解释器运行python -c "import rich; print(rich.file)"。验证“不激活也能用”。 - (Jupyter)在 VS Code 中新建 Notebook,选择项目环境为内核,运行
import sys; sys.executable;再故意切换到另一个环境的内核,尝试import numpy,记录报错并解释原因。 - (进阶 · 字节码)用
dis.dis分别查看def f(x): return x * 2 + 1和一个包含for循环的求和函数的字节码,数一数循环每次迭代大约执行了几条指令,思考为什么sum(range(10**7))比手写for循环快。 - (进阶 · AI Infra)如果你有 NVIDIA GPU:执行
nvidia-smi记下驱动支持的 CUDA 版本,再运行 6.3 节的脚本记下torch.version.cuda,说明二者的关系。没有 GPU 的同学:查阅 pytorch.org 的安装选择器,列出当前 PyTorch 稳定版提供了哪几种 CUDA 版本的 wheel。
下一篇预告
环境搭好了,下一篇 《第02篇|变量、对象与内存模型:一切皆对象、引用与 id》 将正式进入 Python 语言本身。我们会用 id()、type()、sys.getrefcount() 亲手“解剖”Python 对象,搞清楚 a = b 到底是复制还是引用、为什么修改列表会“牵一发而动全身”,以及 CPython 是如何用引用计数管理内存的——这些是理解后面 NumPy 视图与拷贝(第 30 篇)、PyTorch 张量共享存储(第 37 篇)的前提。
专栏一「Python 从零入门」的后续路线:数字与字符串(03)→ 列表与元组(04)→ 字典与集合(05)→ 流程控制(06)→ 函数(07、08)→ 模块与 import 机制(09)→ 文件(10)→ 异常(11)→ 面向对象与数据模型(12~14)。之后依次进入「Python 进阶与工程」「NumPy 数值计算」「PyTorch 与 AI Infra 入门」三个专栏,最终通向 vLLM / SGLang 与 Agent 框架全景(第 48 篇)。
