跳到内容

虚拟环境

在进行 Python 项目开发时,建议使用虚拟环境(或类似的机制)来隔离每个项目安装的包。

信息

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

提示

虚拟环境环境变量是不同的概念。

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

虚拟环境是一个包含某些文件的目录。

信息

本页面将教你如何使用虚拟环境以及它们的工作原理。

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

创建项目

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

我通常会在主目录(home/user)下创建一个名为 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:使用随 Python 一起安装的 venv 模块
  • .venv:在名为 .venv 的新目录中创建虚拟环境

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

$ uv venv

提示

默认情况下,uv 会在名为 .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 二进制文件位于项目内部的 .venv/bin/python(在本例中为 awesome-project),则说明成功了。🎉

$ Get-Command python

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

如果它显示 python 二进制文件位于项目内部的 .venv\Scripts\python(在本例中为 awesome-project),则说明成功了。🎉

升级 pip

提示

如果你使用 uv,你会用它来安装程序而不是 pip,所以无需升级 pip。😎

如果你使用 pip 安装包(Python 默认自带),你应该将其升级到最新版本。

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

提示

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

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

$ python -m pip install --upgrade pip

---> 100%

提示

有时,在尝试升级 pip 时可能会遇到 No module named pip 错误。

如果发生这种情况,请使用以下命令安装并升级 pip

$ python -m ensurepip --upgrade

---> 100%

此命令会在 pip 未安装时进行安装,并确保安装的 pip 版本至少与 ensurepip 中提供的版本一样新。

添加 .gitignore

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

提示

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

提示

在创建虚拟环境后,执行一次即可。

$ echo "*" > .venv/.gitignore
该命令的含义
  • echo "*":会在终端中“打印”文本 *(下一部分会对其稍作修改)
  • >:将左侧命令打印到终端的内容不显示出来,而是写入到右侧的文件中
  • .gitignore:要写入文本的文件名

对于 Git 来说,* 的意思是“所有内容”。因此,它将忽略 .venv 目录中的一切。

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

*

安装包

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

提示

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

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

直接安装包

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

提示

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

配置编辑器

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

例如

提示

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

停用虚拟环境

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

$ deactivate

这样,当你运行 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 时,系统会找到该 Python 程序

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

并使用它。

C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32

这意味着系统现在会首先开始在以下位置查找程序

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

然后再查看其他目录。

因此,当你在终端输入 python 时,系统会找到该 Python 程序

C:\Users\user\code\awesome-project\.venv\Scripts\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-azkaban 的新环境,那么当你运行 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)
  • 以及许多其他事情

总结

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

了解这些细节很可能在未来调试看似复杂的问题时很有用,届时你将知道它们底层是如何工作的。😎