虚拟环境¶
在 Python 项目中工作时,你可能应该使用虚拟环境(或类似机制)来隔离为每个项目安装的包。
信息
如果你已经了解虚拟环境,以及如何创建和使用它们,你可能想跳过本节。 🤓
提示
虚拟环境与环境变量不同。
环境变量是系统中的变量,可供程序使用。
虚拟环境是包含一些文件的目录。
创建项目¶
首先,为你的项目创建一个目录。
我通常的做法是在我的主/用户目录下创建一个名为 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中创建虚拟环境
该命令将在名为 .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
如果它显示 .venv/bin/python 中的 python 二进制文件,位于你的项目内部(在本例中为 awesome-project),那么它就成功了。🎉
$ Get-Command python
C:\Users\user\code\awesome-project\.venv\Scripts\python
如果它显示 .venv\Scripts\python 中的 python 二进制文件,位于你的项目内部(在本例中为 awesome-project),那么它就成功了。🎉
升级 pip¶
提示
如果你使用 uv,你将使用它来安装东西而不是 pip,所以你不需要升级 pip。😎
如果你正在使用 pip 安装包(Python 默认自带),你应该将其升级到最新版本。
许多安装包时遇到的奇怪错误通常可以通过首先升级 pip 来解决。
提示
你通常会在创建虚拟环境后执行一次此操作。
确保虚拟环境已激活(使用上面的命令),然后运行
$ python -m pip install --upgrade pip
---> 100%
添加 .gitignore¶
如果你正在使用 Git(你应该使用),添加一个 .gitignore 文件以将 .venv 中的所有内容从 Git 中排除。
提示
如果你使用 uv 创建了虚拟环境,它已经为你完成了此操作,你可以跳过此步骤。😎
提示
在创建虚拟环境后,执行一次此操作。
$ echo "*" > .venv/.gitignore
该命令的含义
echo "*":将在终端中“打印”文本*(下一部分会稍微改变这一点)>:>左侧命令打印到终端的任何内容都不应打印,而应写入>右侧的文件.gitignore:应写入文本的文件名
对于 Git,* 表示“所有”。因此,它将忽略 .venv 目录中的所有内容。
该命令将创建一个内容为以下的文件 .gitignore
*
安装包¶
激活环境后,你可以在其中安装包。
提示
在安装或升级项目所需的包时执行一次此操作。
如果你需要升级版本或添加新包,你需要再次执行此操作。
直接安装包¶
如果你赶时间,不想使用文件来声明项目包需求,你可以直接安装它们。
提示
将程序所需的包和版本放入文件(例如 requirements.txt 或 pyproject.toml)是一个(非常)好的主意。
$ pip install sqlmodel
---> 100%
如果你有 uv
$ uv pip install sqlmodel
---> 100%
从 requirements.txt 安装¶
如果你有 requirements.txt 文件,你现在可以使用它来安装其中的包。
$ pip install -r requirements.txt
---> 100%
如果你有 uv
$ uv pip install -r requirements.txt
---> 100%
requirements.txt
一个包含一些包的 requirements.txt 文件可能如下所示
sqlmodel==0.13.0
rich==13.7.1
运行程序¶
激活虚拟环境后,你可以运行你的程序,它将使用虚拟环境中的 Python 和你安装在那里的包。
$ python main.py
Hello World
配置编辑器¶
你可能需要使用编辑器,确保将其配置为使用你创建的相同虚拟环境(它可能会自动检测到),这样你就可以获得自动补全和内联错误。
例如
提示
你通常只需执行一次此操作,即在创建虚拟环境时。
停用虚拟环境¶
完成项目工作后,你可以停用虚拟环境。
$ deactivate
这样,当你运行 python 时,它就不会尝试从该虚拟环境运行它,也不会使用安装在那里的包。
准备工作¶
现在你已经准备好开始你的项目工作了。
提示
你想了解上面所有的一切吗?
继续阅读。👇🤓
为什么使用虚拟环境¶
要使用 SQLModel,你需要安装 Python。
之后,你需要安装 SQLModel 和你想要使用的任何其他包。
要安装包,你通常会使用 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 sqlmodel
---> 100%
这将下载一个包含 SQLModel 代码的压缩文件,通常来自 PyPI。
它还将下载SQLModel 所依赖的其他包的文件。
然后它将解压所有这些文件并将它们放在你计算机上的一个目录中。
默认情况下,它会将这些下载和解压的文件放在你的 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,包括不同版本
- 管理你项目的虚拟环境
- 安装包
- 管理你项目的包依赖和版本
- 确保你有一组精确的要安装的包和版本,包括它们的依赖项,这样你就可以确保在生产环境中运行项目时与你在开发时在计算机上运行的项目完全相同,这称为锁定
- 以及许多其他事情
总结¶
如果你阅读并理解了所有这些,那么现在你对虚拟环境的了解比许多开发人员都多。🤓
了解这些细节很可能在将来调试一些看似复杂的问题时很有用,因为你将知道它们在底层是如何工作的。😎