理解Python虚拟环境

Mac 查看python虚拟环境 python虚拟环境安装的包在哪_Python

DavyCloud

努力把事讲清楚

 

107 人赞同了该文章

什么是环境

既然有所谓的 虚拟环境(Virtual Environment),那么首先有必要解释一下,什么是环境。

这里的环境,指的就是 Python 代码的运行环境。它应该包含以下信息:

  • Python 解释器,用哪个解释器来执行代码?
  • Python 库的位置,该去哪里 import 所需要的模块呢?
  • 可执行程序的位置,比如说安装了 pip,那么 pip 命令是在哪里呢?

其中第 1 个是最主要的,后面 2 个基本是围绕它确定的。

如果看了我在 安装 Python 详解 里对安装后的文件夹的说明,应该很清楚了,就是:

  • python.exe
  • Lib 文件夹,包括其中的 site-packages
  • Scripts 文件夹

sys.path

当我们说包的路径就在 Lib 和 site-packages 文件夹里的时候,虽然大多数的情况下就是这样的,但是实际上并不准确。

包的搜寻路径是通过 Python 系统中的一个变量决定的,也就是 sys.path,我们先来打印一下看看:

>>> import sys
>>> from pprint import pprint
>>> pprint(sys.path)
['',   # 注意,别忽视了第 1 个
 'C:\\Users\\Davy\\AppData\\Local\\Programs\\Python\\Python38\\python38.zip',
 'C:\\Users\\Davy\\AppData\\Local\\Programs\\Python\\Python38\\DLLs',
 'C:\\Users\\Davy\\AppData\\Local\\Programs\\Python\\Python38\\lib',
 'C:\\Users\\Davy\\AppData\\Local\\Programs\\Python\\Python38',
 'C:\\Users\\Davy\\AppData\\Roaming\\Python\\Python38\\site-packages',
 'C:\\Users\\Davy\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages']
>>>

“ pprint 是内置的 pretty-print 模块,可以自动换行,让打印结果好看一点。

注意:第 1 项是一个空字符串,它代表的是当前路径,也就是你启动程序的地方。比如说,我们默认情况下进入命令行就自动进入到当前用户的目录,例如 C:\Users\Davy,那么当前目录就是这个。

如果你还感到疑惑,可以用下面的语句打印出绝对路径:

>>> import os.path
>>> os.path.abspath('')
'C:\\Users\\Davy'

除了第一项,这个列表里最常用的就是最后一项,这个在 Python 安装详解 中有过说明。

什么是虚拟环境

知道了什么是环境,再来理解什么是虚拟环境就非常容易了。

简而言之,虚拟环境就是 Python 环境的一个副本。

要得到这么一个副本,首先:

  • 要给它单独找个文件夹存起来
  • 要给它取个名字

这个文件夹的名字也就是这个虚拟环境的名字,在这个文件夹下面有这些东西:

  • 一个 python.exe
  • 一个 Scripts 目录
  • 一个 Lib 目录

这里和普通环境有 2 点不一样的地方:

  • python.exe 也放在了 Scripts 目录下面(原因下面会讲)
  • Lib 目录下面只有 site-packages 目录

让我们来试一下。

venv 模块

在 Python 2.x 的时候,创建虚拟环境还需要安装第三方的 virtualenv,但是自从 Python 3.3 版本之后,标准库里内置了 venv 模块,可以用来创建虚拟环境。

在命令行中使用下面的命令来快速创建一个虚拟环境:

C:\Users\Davy>python -m venv venvdemo

上面的命令会在当前目录下,新建一个名为 venvdemo 的虚拟环境。里面的文件夹:

Mac 查看python虚拟环境 python虚拟环境安装的包在哪_虚拟环境_02

其中 Include 基本不用管,Lib 目录下也没什么特别的,主要就是 Scripts 目录:

Mac 查看python虚拟环境 python虚拟环境安装的包在哪_Python_03

其中多出了 activate 和 deactivate 用来 激活 和 去激活 虚拟环境。

“ activate 有多个后缀的文件,适配多个环境,敲命令的时候不需要带后缀

让我们来激活试试:

C:\Users\Davy>venvdemo\Scripts\activate

Mac 查看python虚拟环境 python虚拟环境安装的包在哪_Mac 查看python虚拟环境_04

 

注意到一点,激活的时候我们需要指定 activate 完整的路径,因为它所在的目录并不在 PATH 环境变量之中。

激活之后,我们就进入了虚拟环境,这时候不管是执行 python 还是 pip 针对的都是虚拟环境里面的。

其实这也没什么神奇的操作,激活只不过就是把虚拟环境的 Scripts 目录临时添加到了 PATH 环境变量的第一位。

Mac 查看python虚拟环境 python虚拟环境安装的包在哪_Python_05

这里也解释了,为啥要把 python.exe 也放到了 Scripts 目录下,因为这样只需要加一个路径到环境变量中即可。

同时这也提醒我们注意,不是只有激活才能进入虚拟环境,我们如果把当前路径切换到了虚拟环境的 Scripts 目录下,启动 python 也是在虚拟环境中了。

继续打印一下 sys.path 看看:

(venvdemo) C:\Users\Davy>python
Python 3.8.1 (tags/v3.8.1:1b293b6, Dec 18 2019, 23:11:46) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from pprint import pprint
>>> import sys
>>> pprint(sys.path)
['',
 'C:\\Users\\Davy\\AppData\\Local\\Programs\\Python\\Python38\\python38.zip',
 'C:\\Users\\Davy\\AppData\\Local\\Programs\\Python\\Python38\\DLLs',
 'C:\\Users\\Davy\\AppData\\Local\\Programs\\Python\\Python38\\lib',
 'C:\\Users\\Davy\\AppData\\Local\\Programs\\Python\\Python38',
 'C:\\Users\\Davy\\venvdemo',
 'C:\\Users\\Davy\\venvdemo\\lib\\site-packages']

可以看到相对于上面普通的系统环境,最下面的两条发生了变化,注意其中的第 4 条路径 'C:\\Users\\Davy\\AppData\\Local\\Programs\\Python\\Python38\\lib',它正是标准库的路径。

我们在执行去激活的时候,就不用再指定完整的路径了。

为什么要有虚拟环境

当我们安装一个 Python 程序或者库的时候,一般情况下我们虽然是想要安装 1 个包,比如说, pip install django。然而实际安装的都是一堆包。这些包默认都会安装到 Python 环境的 site-packages 目录下面。

Mac 查看python虚拟环境 python虚拟环境安装的包在哪_Python_06

下次再安装其它包时,也是如此。因为同一个库,只能在一个环境中存在一份,那么这其中如果发现了某个依赖包已经存在,只能大家公用。

这样下去,说不定哪一天这中间就出现了版本不兼容。

使用虚拟环境

因为虚拟环境的必要性,现在大多数的 Python 开发工具都支持虚拟环境的相关操作。

Mac 查看python虚拟环境 python虚拟环境安装的包在哪_虚拟环境_07

具体每个工具有所不同,但是一般只需要注意一点即可:指定虚拟环境中 python.exe 的位置。一旦确定了它的位置,就确定了环境的位置。也就不用每次都去激活。

“ 仔细观察,虚拟环境中的 python.exe 和系统中的 python.exe 并不完全一样。

保存虚拟环境

我们知道在使用 pip install 的时候可以通过 -r 选项指定一个 requirements 文件,这样就能批量安装所有依赖。

在 requirements 里面可以精确的指定安装包版本,有效地避免不兼容问题。

执行 pip freeze 可以把当前环境安装的包以 requirements 的格式输出。

(venvdemo) C:\Users\Davy>pip freeze
asgiref==3.2.3
Django==3.0.3
pytz==2019.3
sqlparse==0.3.0

把输出结果保存到文件中就可以了,这样我们就精确的得到当前环境的版本信息,可以再其它地方重建这个环境。