虚拟环境¶
当你在 Python 项目中工作时,你可能应该使用**虚拟环境**(或类似机制)来隔离为每个项目安装的软件包。
信息
如果你已经了解虚拟环境,知道如何创建和使用它们,你可能想跳过这一部分。🤓
提示
**虚拟环境**(virtual environment)不同于**环境变量**(environment variable)。
**环境变量**是系统中可供程序使用的变量。
**虚拟环境**是一个包含一些文件的目录。
创建一个项目¶
首先,为你的项目创建一个目录。
我通常的做法是在我的主目录/用户目录下创建一个名为 code 的目录。
然后在里面为每个项目创建一个目录。
// Go to the home directory
$ cd
// Create a directory for all your code projects
$ mkdir code
// Enter into that code directory
$ cd code
// Create a directory for this project
$ mkdir awesome-project
// Enter into that project directory
$ cd awesome-project
创建虚拟环境¶
当你**第一次**开始一个 Python 项目时,在**你的项目内部**创建一个虚拟环境。
提示
你只需要**为每个项目做一次**,而不是每次工作时都做。
要创建虚拟环境,你可以使用 Python 自带的 venv 模块。
$ python -m venv .venv
该命令的含义
python:使用名为python的程序-m:将一个模块作为脚本调用,我们接下来会告诉它哪个模块venv:使用名为venv的模块,它通常与 Python 一起安装.venv:在新目录.venv中创建虚拟环境
该命令在一个名为 .venv 的目录中创建了一个新的虚拟环境。
.venv 或其他名称
你可以在不同的目录中创建虚拟环境,但将其命名为 .venv 是一种惯例。
激活虚拟环境¶
激活新的虚拟环境,以便你运行的任何 Python 命令或安装的任何包都使用它。
提示
每次开始一个新的**终端会话**来处理项目时,都要**这样做**。
$ source .venv/bin/activate
$ .venv\Scripts\Activate.ps1
或者如果你使用 Windows 版的 Bash (例如 Git Bash)
$ source .venv/Scripts/activate
提示
每当你在该环境中安装一个**新包**时,再次**激活**该环境。
这确保了如果你使用由该包安装的**终端(CLI)程序**,你使用的是来自你虚拟环境的那个,而不是任何可能全局安装的其他程序,后者可能版本与你需要的不同。
检查虚拟环境是否激活¶
检查虚拟环境是否已激活(即上一条命令是否生效)。
提示
这是**可选的**,但这是一个很好的方法来**检查**一切是否按预期工作,以及你是否正在使用你打算使用的虚拟环境。
$ which python
/home/user/code/awesome-project/.venv/bin/python
如果它显示 python 二进制文件位于你的项目(本例中为 awesome-project)内部的 .venv/bin/python,那么它就成功了。🎉
$ Get-Command python
C:\Users\user\code\awesome-project\.venv\Scripts\python
如果它显示 python 二进制文件位于你的项目(本例中为 awesome-project)内部的 .venv\Scripts\python,那么它就成功了。🎉
升级 pip¶
提示
如果你使用 uv,你会用它来安装东西而不是 pip,所以你不需要升级 pip。😎
如果你正在使用 pip 来安装包(它随 Python 默认提供),你应该**升级**到最新版本。
许多安装包时的奇怪错误只需先升级 pip 就能解决。
提示
你通常只需在创建虚拟环境后**做一次**。
确保虚拟环境已激活(使用上面的命令),然后运行
$ python -m pip install --upgrade pip
---> 100%
添加 .gitignore¶
如果你正在使用 **Git**(你应该使用),添加一个 .gitignore 文件以从 Git 中排除 .venv 中的所有内容。
提示
如果你使用 uv 来创建虚拟环境,它已经为你做了这件事,你可以跳过这一步。😎
提示
在创建虚拟环境后**做一次**即可。
$ echo "*" > .venv/.gitignore
该命令的含义
echo "*":将在终端中“打印”文本*(下一部分会对此有所改变)>:>左侧命令打印到终端的任何内容都不应打印,而应写入>右侧的文件中.gitignore:应该写入文本的文件名
在 Git 中,* 的意思是“所有东西”。所以,它会忽略 .venv 目录中的所有东西。
该命令将创建一个名为 .gitignore 的文件,内容如下
*
安装包¶
激活环境后,你可以在其中安装软件包。
提示
在安装或升级项目所需的包时,**做一次**这个操作。
如果需要升级版本或添加新包,你会**再做一次**。
直接安装包¶
如果你赶时间,不想用文件来声明项目的包需求,你可以直接安装它们。
提示
将你的程序需要的包和版本放在一个文件里(例如 requirements.txt 或 pyproject.toml)是一个(非常)好的主意。
$ pip install "fastapi[standard]"
---> 100%
如果你有 uv
$ uv pip install "fastapi[standard]"
---> 100%
从 requirements.txt 安装¶
如果你有一个 requirements.txt,现在你可以用它来安装其中的软件包。
$ pip install -r requirements.txt
---> 100%
如果你有 uv
$ uv pip install -r requirements.txt
---> 100%
requirements.txt
一个包含一些包的 requirements.txt 可能看起来像这样
fastapi[standard]==0.113.0
pydantic==2.8.0
运行你的程序¶
激活虚拟环境后,你可以运行你的程序,它将使用你虚拟环境中的 Python 以及你在那里安装的软件包。
$ python main.py
Hello World
配置你的编辑器¶
你可能会使用一个编辑器,请确保你将其配置为使用你创建的同一个虚拟环境(它可能会自动检测到),这样你就可以获得自动补全和内联错误提示。
例如
提示
你通常只需在创建虚拟环境时**做一次**。
停用虚拟环境¶
完成项目工作后,你可以**停用**虚拟环境。
$ deactivate
这样,当你运行 python 时,它不会尝试运行那个虚拟环境中安装了相应软件包的 Python。
准备工作¶
现在你已经准备好开始你的项目了。
提示
你想了解上面这些都是什么吗?
继续阅读。👇🤓
为什么需要虚拟环境¶
要使用 FastAPI,你需要安装 Python。
之后,你需要**安装** FastAPI 和任何其他你想使用的**软件包**。
要安装软件包,你通常会使用 Python 自带的 pip 命令(或类似替代品)。
然而,如果你只是直接使用 pip,软件包将被安装在你的**全局 Python 环境**中(即 Python 的全局安装位置)。
问题所在¶
那么,在全局 Python 环境中安装软件包有什么问题呢?
在某个时候,你可能会编写许多依赖于**不同软件包**的不同程序。而且你工作的一些项目将依赖于同一个软件包的**不同版本**。😱
例如,你可以创建一个名为 philosophers-stone 的项目,这个程序依赖于另一个名为 **harry 的包,使用版本 1**。所以,你需要安装 harry。
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
然后,过了一段时间,你创建了另一个名为 prisoner-of-azkaban 的项目,这个项目也依赖于 harry,但这个项目需要 **harry 版本 3**。
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
但现在问题是,如果你全局安装这些包(在全局环境中),而不是在本地的**虚拟环境**中,你就必须选择安装哪个版本的 harry。
如果你想运行 philosophers-stone,你需要先安装 harry 版本 1,例如使用
$ pip install "harry==1"
然后你的全局 Python 环境中就会安装 harry 版本 1。
flowchart LR
subgraph global[global env]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -->|requires| harry-1
end
但是如果你想运行 prisoner-of-azkaban,你就需要卸载 harry 版本 1 并安装 harry 版本 3(或者直接安装版本 3 会自动卸载版本 1)。
$ pip install "harry==3"
然后你的全局 Python 环境中就会安装 harry 版本 3。
如果你再尝试运行 philosophers-stone,它很可能**无法工作**,因为它需要 harry 版本 1。
flowchart LR
subgraph global[global env]
harry-1[<strike>harry v1</strike>]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --> |requires| harry-3
end
提示
在 Python 包中,通常会尽力在**新版本**中**避免破坏性更改**,但为了安全起见,最好是有意地安装新版本,并且在可以运行测试以检查一切正常时才这样做。
现在,想象一下你所有**项目依赖**的**许多**其他**包**。这很难管理。你最终可能会用一些**不兼容版本**的包来运行某些项目,却不知道为什么某些东西不工作。
此外,根据你的操作系统(例如 Linux、Windows、macOS),它可能已经预装了 Python。在这种情况下,它可能预装了一些**系统所需**的特定版本的软件包。如果你在全局 Python 环境中安装软件包,你可能会**破坏**操作系统自带的一些程序。
软件包安装在哪里¶
当你安装 Python 时,它会在你的计算机中创建一些包含文件的目录。
其中一些目录负责存放你安装的所有软件包。
当你运行
// Don't run this now, it's just an example 🤓
$ pip install "fastapi[standard]"
---> 100%
这会下载一个包含 FastAPI 代码的压缩文件,通常来自 PyPI。
它还会**下载** FastAPI 依赖的其他包的文件。
然后它会**解压**所有这些文件并将它们放在你计算机的一个目录中。
默认情况下,它会将这些下载和解压的文件放在你 Python 安装自带的目录中,这就是**全局环境**。
什么是虚拟环境¶
解决将所有软件包放在全局环境中的问题的方案是为你工作的**每个项目使用一个虚拟环境**。
虚拟环境是一个**目录**,与全局环境非常相似,你可以在其中安装项目的软件包。
这样,每个项目都会有自己带有自己软件包的虚拟环境(.venv 目录)。
flowchart TB
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) --->|requires| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --->|requires| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
激活虚拟环境意味着什么¶
当你激活一个虚拟环境时,例如使用
$ source .venv/bin/activate
$ .venv\Scripts\Activate.ps1
或者如果你使用 Windows 版的 Bash (例如 Git Bash)
$ source .venv/Scripts/activate
该命令将创建或修改一些环境变量,这些变量将对后续命令可用。
其中一个变量是 PATH 变量。
提示
你可以在环境变量部分了解更多关于 PATH 环境变量的信息。
激活虚拟环境会将其路径 .venv/bin (在 Linux 和 macOS 上) 或 .venv\Scripts (在 Windows 上) 添加到 PATH 环境变量中。
假设在激活环境之前,PATH 变量看起来是这样的
/usr/bin:/bin:/usr/sbin:/sbin
这意味着系统会在以下位置查找程序
/usr/bin/bin/usr/sbin/sbin
C:\Windows\System32
这意味着系统会在以下位置查找程序
C:\Windows\System32
激活虚拟环境后,PATH 变量会变成这样
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
这意味着系统现在会首先在
/home/user/code/awesome-project/.venv/bin
中寻找程序,然后再去其他目录寻找。
所以,当你在终端输入 python 时,系统会找到位于
/home/user/code/awesome-project/.venv/bin/python
的 Python 程序,并使用它。
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
这意味着系统现在会首先在
C:\Users\user\code\awesome-project\.venv\Scripts
中寻找程序,然后再去其他目录寻找。
所以,当你在终端输入 python 时,系统会找到位于
C:\Users\user\code\awesome-project\.venv\Scripts\python
的 Python 程序,并使用它。
一个重要的细节是,它会将虚拟环境的路径放在 PATH 变量的**开头**。系统会在找到任何其他可用的 Python **之前**找到它。这样,当你运行 python 时,它将使用**来自虚拟环境**的 Python,而不是任何其他 python(例如,来自全局环境的 python)。
激活虚拟环境还会改变其他一些事情,但这是它做的最重要的事情之一。
检查虚拟环境¶
当你检查虚拟环境是否激活时,例如使用
$ which python
/home/user/code/awesome-project/.venv/bin/python
$ Get-Command python
C:\Users\user\code\awesome-project\.venv\Scripts\python
这意味着将要使用的 python 程序是**虚拟环境中的那个**。
你在 Linux 和 macOS 上使用 which,在 Windows PowerShell 上使用 Get-Command。
该命令的工作方式是,它会检查 PATH 环境变量,**按顺序遍历每个路径**,寻找名为 python 的程序。一旦找到,它就会**显示该程序的路径**。
最重要的是,当你调用 python 时,这正是将被执行的那个“python”。
所以,你可以确认你是否在正确的虚拟环境中。
提示
很容易激活一个虚拟环境,得到一个 Python,然后**转到另一个项目**。
而第二个项目**将无法工作**,因为你正在使用**不正确的 Python**,它来自另一个项目的虚拟环境。
能够检查正在使用哪个 python 是很有用的。🤓
为什么要停用虚拟环境¶
例如,你可能正在做一个项目 philosophers-stone,**激活该虚拟环境**,安装包并使用该环境工作。
然后你想在**另一个项目** prisoner-of-azkaban 上工作。
你进入那个项目
$ cd ~/code/prisoner-of-azkaban
如果你不为 philosophers-stone 停用虚拟环境,当你在终端中运行 python 时,它将尝试使用来自 philosophers-stone 的 Python。
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// Error importing sirius, it's not installed 😱
Traceback (most recent call last):
File "main.py", line 1, in <module>
import sirius
但如果你停用虚拟环境并为 prisoner-of-askaban 激活新的虚拟环境,那么当你运行 python 时,它将使用 prisoner-of-azkaban 中虚拟环境的 Python。
$ cd ~/code/prisoner-of-azkaban
// You don't need to be in the old directory to deactivate, you can do it wherever you are, even after going to the other project 😎
$ deactivate
// Activate the virtual environment in prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Now when you run python, it will find the package sirius installed in this virtual environment ✨
$ python main.py
I solemnly swear 🐺
替代方案¶
这是一个简单的指南,帮助你入门并教你一切**底层**是如何工作的。
管理虚拟环境、包依赖(需求)、项目有许多**替代方案**。
一旦你准备好并想使用一个工具来**管理整个项目**,包括包依赖、虚拟环境等,我建议你试试 uv。
uv 可以做很多事情,它可以:
- 为你**安装 Python**,包括不同版本
- 为你的项目管理**虚拟环境**
- 安装**软件包**
- 为你的项目管理包的**依赖和版本**
- 确保你有一套**精确的**软件包和版本可以安装,包括它们的依赖项,这样你就可以确保在生产环境中运行项目的方式与你在计算机上开发时完全相同,这被称为**锁定(locking)**
- 还有许多其他事情
总结¶
如果你阅读并理解了所有这些,现在你对虚拟环境的了解比许多开发者**都要多**。🤓
了解这些细节很可能会在未来某个时候调试一些看似复杂的问题时派上用场,因为你将知道**这一切的底层是如何工作的**。😎