Skip to content

虚拟环境

当你在 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: 使用名为 venv 的模块,通常随 Python 一起安装
  • .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

如果它显示 python 二进制文件在 .venv/bin/python,在你的项目内部(在这个例子中是 awesome-project),那么它就成功了。🎉

$ Get-Command python

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

如果它显示 python 二进制文件在 .venv\Scripts\python,在你的项目内部(在这个例子中是 awesome-project),那么它就成功了。🎉

升级 pip

Tip

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

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

在安装包时遇到的许多奇怪错误只需先升级pip就能解决。

Tip

你通常只需要做一次这个操作,就在你创建虚拟环境之后。

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

$ python -m pip install --upgrade pip

---> 100%

添加.gitignore

如果你使用**Git**(你应该使用),添加一个.gitignore文件以排除.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[哲人石项目]
        stone(哲人石) --->|需要| harry-1
        subgraph venv1[.venv]
            harry-1[哈利 v1]
        end
    end
    subgraph azkaban-project[阿兹卡班囚徒项目]
        azkaban(阿兹卡班囚徒) --->|需要| harry-3
        subgraph venv2[.venv]
            harry-3[哈利 v3]
        end
    end
    stone-project ~~~ azkaban-project

激活虚拟环境意味着什么

当你激活一个虚拟环境时,例如使用以下命令:

//// 标签 | Linux, macOS

$ source .venv/bin/activate

////

//// 标签 | Windows PowerShell

$ .venv\Scripts\Activate.ps1

////

//// 标签 | Windows Bash

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

$ source .venv/Scripts/activate

////

该命令将创建或修改一些环境变量,这些变量将对后续命令可用。

其中之一是 PATH 变量。

/// 提示

你可以在环境变量部分了解更多关于 PATH 环境变量的信息。

///

激活虚拟环境会将它的路径 .venv/bin(在 Linux 和 macOS 上)或 .venv\Scripts(在 Windows 上)添加到 PATH 环境变量中。

假设在激活环境之前,PATH 变量看起来像这样:

//// 标签 | Linux, macOS

/usr/bin:/bin:/usr/sbin:/sbin

这意味着系统会在以下位置查找程序:

  • /usr/bin
  • /bin
  • /usr/sbin
  • /sbin

////

//// 标签 | Windows

C:\Windows\System32

这意味着系统会在以下位置查找程序:

  • C:\Windows\System32

////

激活虚拟环境后,PATH 变量将看起来像这样:

//// 标签 | Linux, macOS

/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 程序并使用它。

////

//// 标签 | Windows

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)。

激活虚拟环境还会改变其他几件事,但这是它做的最重要的事情之一。

检查虚拟环境

当你检查虚拟环境是否处于活动状态时,例如使用以下命令:

//// 标签 | Linux, macOS, Windows Bash

$ which python

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

////

//// 标签 | Windows PowerShell

$ 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

// 导入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,包括不同版本
  • 管理你项目的**虚拟环境**
  • 安装**包**
  • 管理你项目的包**依赖和版本**
  • 确保你拥有一套**精确**的包和版本进行安装,包括它们的依赖,这样你可以确保在生产环境中运行项目时与在开发计算机上完全一致,这被称为**锁定**
  • 以及许多其他功能

结论

如果你阅读并理解了所有这些内容,那么现在**你对虚拟环境的了解比许多开发者都要多**。🤓

了解这些细节很可能在未来某个时刻对你调试看似复杂的问题时非常有用,因为你会知道**这一切在底层是如何工作的**。😎