和其他大多数现代编程语言一样,Python对包和模块的下载、存储以及管理有其自己的一套方法。Python的包一般存在几个地方。比如,大部分系统包会存在sys.prefix指定的路径下。在Mac OS X下这个路径为:
>>> import sys
>>> sys.prefix
'/System/Library/Frameworks/Python.framework/Versions/3.5'
通常情况下我们更关心第三方包的安装位置,比如easy_install或pip会将包存放在 site.getsitepackages所指定的路径下:
>>> import site
>>> site.getsitepackages()
[
'/System/Library/Frameworks/Python.framework/Versions/3.5/Extras/lib/python',
'/Library/Python/3.5/site-packages'
]
这里就带来了一个问题,当我们同时开发多个工程时,不同的工程会将第三方的包存放在相同的路径下。这就意味着,如果有两个工程依赖同一个包,但是所需要的版本却不一样,比如工程A依赖v1.0.0,而工程B依赖v2.0.0。由于Python无法根据版本来区分包的安装路径,所以这里就会发生版本冲突。这也就是本文所要介绍的虚拟环境(virtualenv/venv)所要解决的问题。
什么是虚拟环境?
Python虚拟环境的主要目的是为了给不同的工程创建互相独立的运行环境。在虚拟环境下,每一个工程都有自己的依赖包,而与其它的工程无关。不同的虚拟环境中同一个包可以有不同的版本。并且,虚拟环境的数量没有限制,我们可以轻松地用virtualenv或者pyenv等工具来创建多个虚拟环境。
虚拟环境的使用
安装虚拟环境工具的方法非常简单,
$ pip install virtualenv
然后,我们创建一个名字叫“env”的虚拟环境:
$ virtualenv env
这条命令会自动创建一个叫“env”的目录,并在其中生成如下的目录结构:
├── bin
│ ├── activate
│ ├── activate.csh
│ ├── activate.fish
│ ├── easy_install
│ ├── easy_install-3.5
│ ├── pip
│ ├── pip3
│ ├── pip3.5
│ ├── python -> python3.5
│ ├── python3 -> python3.5
│ └── python3.5 -> /Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5
├── include
├── lib
│ └── python3.5
│ └── site-packages
└── pyvenv.cfg
这些目录包括:
- bin: 用于管理虚拟环境的文件
- include: 编译Python包时所需要的C头文件
- lib: Python自带及第三方的库
这其中还包含有一些Python的工具和可执行文件等副本。这些文件用来保证Python代码可以独立于系统环境而运行。
bin目录下有一个重要的脚本文件activate,这个脚本就是用来将其所在的虚拟环境设置为当前Python的运行环境:
$ source env/bin/activate
(env) $
可以看到,在运行完这行命令后,shell的提示符前会出现虚拟环境的名字,表示我们已经进入了这个环境中。
为了验证这一点,我们以bcrypt模块为例。首先我们要在全局系统环境中安装这个模块。在测试之前,我们需要先通过deactivate命令退出当前的虚拟环境:
(env) $ deactivate
$
现在我们的shell提示符回归到了正常状态,同时Python的环境也切换到了全局的系统环境。
现在我们来安装bcrypt并用它来生成一个密码的hash值:
$ pip -q install bcrypt
$ python -c "import bcrypt; print(bcrypt.hashpw('password'.encode('utf-8'), bcrypt.gensalt()))"
$2b$12$vWa/VSvxxyQ9d.WGgVTdrell515Ctux36LCga8nM5QTW0.4w8TXXi
然后我们切换到虚拟环境并试图运行同样的命令:
$ source env/bin/activate
(env) $ python -c "import bcrypt; print(bcrypt.hashpw('password'.encode('utf-8'), bcrypt.gensalt()))"
Traceback (most recent call last):
File
"