跳转至

虚拟环境

在 Python 项目中工作时,你很可能需要使用虚拟环境(或类似机制)来隔离为每个项目安装的包。

Info

如果你已经了解虚拟环境,知道如何创建和使用它们,可以跳过本节。🤓

Tip

虚拟环境环境变量不同。

环境变量是系统中可供程序使用的变量。

虚拟环境是一个包含若干文件的目录。

Info

本页将教你如何使用虚拟环境及其工作原理。

如果你准备采用能管理一切的工具(包括安装 Python),可以尝试 uv

创建项目

首先,为你的项目创建一个目录。

我通常的做法是在主目录/用户目录下创建一个名为 code 的目录。

然后在该目录下为每个项目创建一个子目录。

// 进入主目录
$ cd
// 为所有代码项目创建目录
$ mkdir code
// 进入该代码目录
$ cd code
// 为该项目创建目录
$ mkdir awesome-project
// 进入该项目目录
$ cd awesome-project

创建虚拟环境

当你首次开始处理 Python 项目时,在项目内部创建一个虚拟环境

Tip

每个项目只需执行一次,无需每次工作时都创建。

要创建虚拟环境,可以使用 Python 自带的 venv 模块。

$ python -m venv .venv
该命令的含义
  • python:使用名为 python 的程序
  • -m:将模块作为脚本调用,接下来指定要调用的模块
  • venv:使用通常随 Python 安装的 venv 模块
  • .venv:在新的 .venv 目录中创建虚拟环境

如果已安装 uv,可以用它来创建虚拟环境。

$ uv venv

Tip

默认情况下,uv 会在名为 .venv 的目录中创建虚拟环境。

但你可以通过传递目录名作为附加参数来自定义。

该命令在名为 .venv 的目录中创建一个新的虚拟环境。

.venv 或其他名称

你可以在不同的目录中创建虚拟环境,但约定俗成将其命名为 .venv

激活虚拟环境

激活新的虚拟环境,以便你运行的任何 Python 命令或安装的包都使用该环境。

Tip

每次开始新的终端会话处理项目时都要执行此操作。

$ source .venv/bin/activate
$ .venv\Scripts\Activate.ps1

或者如果你使用 Windows 的 Bash(例如 Git Bash):

$ source .venv/Scripts/activate

Tip

每次在该环境中安装新包后,重新激活该环境。

这可以确保如果你使用该包安装的终端(CLI)程序,你使用的是虚拟环境中的版本,而不是任何其他可能全局安装的版本(可能与你所需的版本不同)。

检查虚拟环境是否激活

检查虚拟环境是否已激活(即上一个命令是否生效)。

Tip

这是可选的,但这是检查一切是否按预期工作以及你是否在使用预期的虚拟环境的好方法。

$ which python

/home/user/code/awesome-project/.venv/bin/python

如果它显示位于项目内(此处为 awesome-project.venv/bin/pythonpython 二进制文件,则表示成功。🎉

$ Get-Command python

C:\Users\user\code\awesome-project\.venv\Scripts\python

如果它显示位于项目内(此处为 awesome-project.venv\Scripts\pythonpython 二进制文件,则表示成功。🎉

升级 pip

Tip

如果你使用 uv,你会用它来安装东西而不是 pip,因此不需要升级 pip。😎

如果你使用 pip 安装包(它默认随 Python 一起提供),你应该将其升级到最新版本。

许多安装包时出现的奇特错误只需先升级 pip 即可解决。

Tip

通常只需在创建虚拟环境后立即执行一次

确保虚拟环境已激活(使用上述命令),然后运行:

$ python -m pip install --upgrade pip

---> 100%

添加 .gitignore

如果你使用 Git(你应该使用),添加一个 .gitignore 文件以从 Git 中排除 .venv 中的所有内容。

Tip

如果你使用 uv 创建虚拟环境,它已经为你完成了此操作,可以跳过此步骤。😎

Tip

在创建虚拟环境后立即执行一次

$ echo "*" > .venv/.gitignore
该命令的含义
  • echo "*":将在终端中“打印”文本 *(下一部分略有改变)
  • >> 左侧命令打印到终端的任何内容不应打印,而应写入 > 右侧的文件
  • .gitignore:应写入文本的文件名

* 对于 Git 表示“所有内容”。因此,它将忽略 .venv 目录中的所有内容。

该命令将创建一个内容如下的 .gitignore 文件:

*

安装包

激活环境后,你可以在其中安装包。

Tip

在安装或升级项目所需的包时执行一次

如果需要升级版本或添加新包,则需再次执行此操作

直接安装包

如果你赶时间,不想使用文件来声明项目的包要求,可以直接安装它们。

Tip

将程序所需的包和版本放入文件(例如 requirements.txtpyproject.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

配置你的编辑器

你可能会使用编辑器,请确保将其配置为使用你创建的相同虚拟环境(它可能会自动检测),以便获得自动补全和内联错误。

例如:

Tip

通常只需在创建虚拟环境时执行一次

停用虚拟环境

完成项目工作后,可以停用虚拟环境。

$ deactivate

这样,当你运行 python 时,它不会尝试使用该虚拟环境及其中安装的包。

准备工作

现在你已准备好开始处理你的项目。

Tip

你想了解以上所有内容吗?

继续阅读。👇🤓

为什么需要虚拟环境

要使用 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

Tip

在 Python 包中,通常尽量避免在新版本中引入破坏性变更,但最好还是谨慎行事,有意安装新版本,并在可以运行测试以检查一切是否正常时进行。

现在,想象一下你的所有项目都依赖许多其他的情况。这非常难以管理。你最终可能会运行一些具有不兼容包版本的项目,并且不知道某些东西为什么无法工作。

此外,根据你的操作系统(例如 Linux、Windows、macOS),它可能已经预装了 Python。在这种情况下,它可能已经预装了一些你的系统所需的特定版本的包。如果你在全局 Python 环境中安装包,最终可能会破坏操作系统附带的某些程序。

包安装在哪里

当你安装 Python 时,它会在你的计算机上创建一些包含若干文件的目录。

其中一些目录负责存放你安装的所有包。

当你运行:

// 现在不要运行,这只是一个示例 🤓
$ 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 变量。

Tip

你可以在环境变量部分了解更多关于 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”。

因此,你可以确认是否在正确的虚拟环境中。

Tip

很容易激活一个虚拟环境,获得一个 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

// 导入 sirius 错误,它未安装 😱
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

// 你不需要在旧目录中停用,你可以在任何地方执行,甚至在转到其他项目之后 😎
$ deactivate

// 激活 prisoner-of-azkaban/.venv 中的虚拟环境 🚀
$ source .venv/bin/activate

// 现在当你运行 python 时,它将找到安装在此虚拟环境中的包 sirius ✨
$ python main.py

I solemnly swear 🐺

替代方案

这是一个简单的指南,让你入门并教你底层的一切是如何工作的。

有许多替代方案可以管理虚拟环境、包依赖项(需求)、项目。

一旦你准备好并想使用一个工具来管理整个项目、包依赖项、虚拟环境等,我建议你尝试 uv

uv 可以做很多事情,它可以:

  • 为你安装 Python,包括不同版本
  • 管理你的项目的虚拟环境
  • 安装
  • 管理你的项目的包依赖项和版本
  • 确保你有一组精确的包和版本要安装,包括它们的依赖项,这样你可以确保在生产环境中运行项目与在开发计算机上运行完全相同,这称为锁定
  • 以及许多其他事情

结论

如果你阅读并理解了所有这些,现在你比许多开发人员更了解虚拟环境。🤓

了解这些细节很可能在未来的某个时间点对你有用,当你调试一些看似复杂的东西时,但你会知道底层的一切是如何工作的。😎