Python包管理和虚拟环境


纸上得来终觉浅,绝知此事要躬行。

Python包管理和虚拟环境


1. 包管理

使用Python语言进行编程的时候,不可避免的需要安装和使用第三方的包,怎么样方便且易用对于我们来说就是一个很重要的事情了,下面就开始介绍几个对应对的工具。

  • [-] 安装第三方包的方法

    • [推荐] 通过Python社区开发的pipeasy_install等工具
    • [禁止] 使用系统本身自带的包管理器yumapt-get
    • [特定] 通过源码安装,如执行python setup.py install命令
  • [1] easy_install 工具

    • 它是setuptools包里自带的一个命令,不用额外安装
    • 使用它实际上是在调用setuptools来完成安装模块的工作`
    • 终端上的输出不够友好
    • 安装的包不能缓存使用,每次都要下载甚至编译
    • 不能集中管理项目依赖列表,就是包得一个一个安装
    • 它提供的Python应用打包部署方式(egg)已经落伍了
    • 它只支持安装,没有提供卸载、展示当前已安装的包列表等功能
  • [2] pip 工具的优势

    • 更好的终端输出效果
    • 它支持多种版本工具格式的包的下载和安装
    • 它能非常好地支持虚拟环境virtualenv工具
    • 它已经内置到2.7.93.4及其以上的版本里面
    • 它可以集中管理项目依赖列表,使用-r选项安装这些依赖
    • 它支持二进制包使用wheel格式,而easy_install不支持
    • 它只支持安装,没有提供卸载、展示当前已安装的包列表等功能
    • 可以对下载的包进行缓存,下次直接从缓存目录取而不需要下载了
# 使用pip工具安装
$ sudo apt-get install python-pip -yq

# 参数-q表示静默安装减少过程输出
$ sudo pip install pip -U -q

2. 虚拟环境工具

主要介绍我们在使用 Python 工具时使用到的虚拟环境工具

2.1 virtualenv

virtualenv模块用于,在一台机器上创建多个Python虚拟运行环境,多个Python环境相互独立且互不影响,只是对固定版本Python的一个复制,所以没有权限问题。

# 参数列表
$ virtualenv --help
Usage: virtualenv [OPTIONS] DEST_DIR

Options:
  --version               显示程序的版本号并退出
  -h, --help              显示此帮助消息并退出
  -v, --verbose           输出详细信息
  -q, --quiet             静默输出
  -p PYTHON_EXE           要使用的Python解释器
  --clear                 清空非root用户的安装并重头开始创建隔离环境
  --no-site-packages      令隔离环境不能访问系统全局的site-packages目录(默认)
  --system-site-packages  令隔离环境可以访问系统全局的site-packages目录
  --no-setuptools         不要在新的virtualenv中安装setuptools工具
  --no-pip                不要在新的virtualenv中安装pip工具
  --download              从PyPI下载预安装的包
  --no-download           不要从PyPI下载预安装的包
  --prompt=PROMPT         提供了一个可选的提示前缀环境
# 安装virtualenv工具
$ sudo pip install virtualenv

# 创建Python虚拟环境
$ virtualenv venv
New python executable in /home/escape/venv/bin/python
Installing setuptools, pip, wheel...done.

# 激活虚拟环境
$ source venv/bin/activate

(venv)$  which python
/home/escape/venv/bin/python

# 退出虚拟环境
(venv)$  deactivate
# 加上--no-site-packages参数; 得到干净的Python运行环境
# 这样已经安装到系统Python环境中的所有第三方包都不会复制过来
$ virtualenv --no-site-packages venv

# 创建不用版本的venv环境
# 可以配合pyenv来创建不同Python版本的运行环境
$ virtualenv -p `which python3.6` venv
$ virtualenv -p `which python3.6` --no-site-packages venv

2.2 venv

venv模块是在Python3.3中引入的一个创建虚拟环境的模块,它是经过virtualenv改造进入的标准库的,主要用于创建虚拟环境。

# Python3.6开始pyvenv脚本已经不可用
$ pyvenv /path/to/new/virtual/environment
# Python3.6之后只能使用如下方式创建虚拟环境
$ python3 -m venv /path/to/new/virtual/environment

# 新版本使用操作步骤
$ python -m venv .env
$ source .env/bin/activate
$ pip install spacy

2.3 pyenv

pipenv模块是Python项目的依赖管理器,其实它并没有什么先进的理念或者技术,类似于Node.jsnpm工具。虽然pip可以安装Python的第三方包,但是推荐使用pipenv,因为它是一种更加高级的工具,可以简化依赖关系等优点。

# 一样进入虚拟环境
"[ -d `pipenv --venv` ] && source `pipenv --venv`/bin/activate && reset"
  • pipenv
    • [工具特点]
    • 自动更新pip工具
    • 自动管理Pipfile新安装和删除的包
    • 根据Pipfile自动寻找项目根目录
    • 如果Pipfile不存在,可以自动生成PipfilePipfile.lock
    • 自动在项目目录的.venv目录创建虚拟环境,可通过设置WORKON_HOME改变
    • [基础理念]
    • pipenv默认也包含了对.env文件的支持
    • 提供版本锁支持,存为Pipfile.lock文件
    • Pipfile文件是TOML格式而不是requirements.txt这样的纯文本
    • 一个项目对应一个Pipfile,支持开发环境与正式环境区分,默认提供defaultdevelopment区分
#####################
# 以Mac下Python3为例 #
#####################

# 安装Python3版本
$ brew install python
$ python -m pip install --upgrade --force-reinstall pip --user

# 推荐安装在个人目录下
$ pip install pipenv --user

# 把用户目录下bin放在最前面,这样可以直接使用pipenv了
$ export PATH="/Users/Escape/Library/Python/3.7/bin:$PATH"
#################
# 使用pipenv工作 #
#################

# 创建一个工作目录
$ mkdir test_pipenv; cd test_pipenv

# 创建一个虚拟环境
$ pipenv install
......
Installing dependencies from Pipfile.lock (a65489)...
🐍 ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00
To activate this project virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.

# 还是mac自带的Python
$ which python

# 激活虚拟环境
$ pipenv shell

# 已经在虚拟环境里了
$ which python
/Users/Escape/.local/share/virtualenvs/test_pipenv-wGd2NbSj/bin/python

# Pipfile用来保存依赖关系的
$ ll
total 16
-rw-r--r--  1 Escape  staff   138B  9 10 12:52 Pipfile
-rw-r--r--  1 Escape  staff   453B  9 10 12:52 Pipfile.lock

# 退出虚拟环境
$ exit
#################
# 使用pipenv工作 #
#################

# 安装2个包,分别为elasticsearch-dsl和requests库
# 现在Pipfile.lock已经更新了,包含了相关依赖的包信息
# 可添加--two或--three标志到下面的最后一个命令,分别使用Python2或3来初始化你的项目
$ pipenv install elasticsearch-dsl requests

# 可以看一下依赖关系
$ pipenv graph                                                                                                                            Escape@EscapeLife
elasticsearch-dsl==6.2.1
  - elasticsearch [required: >=6.0.0,<7.0.0, installed: 6.3.1]
    - urllib3 [required: >=1.21.1, installed: 1.23]
  - ipaddress [required: Any, installed: 1.0.22]
  - python-dateutil [required: Any, installed: 2.7.3]
    - six [required: >=1.5, installed: 1.11.0]
  - six [required: Any, installed: 1.11.0]
requests==2.19.1
  - certifi [required: >=2017.4.17, installed: 2018.8.24]
  - chardet [required: >=3.0.2,<3.1.0, installed: 3.0.4]
  - idna [required: >=2.5,<2.8, installed: 2.7]
  - urllib3 [required: >=1.21.1,<1.24, installed: 1.23]

# 可以看到两者都依赖了urllib3
# 虽然现在pipenv不能直接卸载包及其依赖,但我们可以婉转的实现
$ pipenv uninstall `pipenv graph --json | python depends.py requests`

# 其中depends.py脚本会解析依赖关系,排除其他包依赖的项目然后删除
$ cat depends.py
import sys
import json
package = sys.argv[1]
other_dependencies = set()
removing_dependencies = set([package])
for i in json.load(sys.stdin):
    for p in i['dependencies']:
        key = p['key']
        if i['package']['key'] == package:
            removing_dependencies.add(key)
        else:
            other_dependencies.add(key)
print(' '.join(removing_dependencies - other_dependencies))

# 再看一下现在环境中的包依赖关系
$ elasticsearch-dsl==6.1.0
  - elasticsearch [required: >=6.0.0,<7.0.0, installed: 6.1.1]
    - urllib3 [required: >=1.21.1,<1.23, installed: 1.22]
  - ipaddress [required: Any, installed: 1.0.19]
  - python-dateutil [required: Any, installed: 2.6.1]
    - six [required: >=1.5, installed: 1.10.0]
  - six [required: Any, installed: 1.10.0]
###################
# pipenv的其他功能 #
###################

# 可以激活虚拟环境并使用shell命令
$ pipenv run which python

# 检查装的包的安全性
$ pipenv check

# 传统的看文档的方法
$ pipenv --man

# 代码Flake8检查
$ pipenv check --style depends.py
#################
# pipenv用法示例 #
#################

# Create a new project using Python 3.7, specifically:
$ pipenv --python 3.7

# Remove project virtualenv (inferred from current directory):
$ pipenv --rm

# Install all dependencies for a project (including dev):
$ pipenv install --dev

# Create a lockfile containing pre-releases:
$ pipenv lock --pre

# Show a graph of your installed dependencies:
$ pipenv graph

# Check your installed dependencies for security vulnerabilities:
$ pipenv check

# Install a local setup.py into your virtual environment/Pipfile:
$ pipenv install -e .

# Use a lower-level pip command:
$ pipenv run pip freeze

2.4 autoenv

autoenv模块可以让你再切换文件目录的时候,自动激活和退出虚拟环境等一系列定制操作。虚拟环境的终极方案 = autoenv + pipenv

# 安装autoenv模块
$ sudo pip install autoenv

# 激活autoenv模块
$ source /usr/local/bin/activate.sh

# 创建项目目录
$ mkdir test
$ cd test

# 写autoenv的配置文件,需要使用绝对路径
$ touch .env
$ echo "source /home/escape/venv/bin/activate" > .env

# 重新进入该项目模块
$ cd ..
$ cd test
autoenv:
autoenv: WARNING:
autoenv: This is the first time you are about to source /home/escape/test/.env:
autoenv:
autoenv:     --- (begin contents) ---------------------------------------
autoenv:     source /home/escape/venv/bin/activate
autoenv:
autoenv:     --- (end contents) -----------------------------------------
autoenv:
autoenv: Are you sure you want to allow this? (y/N) y

# 自动切换虚拟环境
(venv) $

3. 版本管理工具

如果你只单单使用pyenv的话,那么它就是一个版本管理工具而已。但是,如果配合上它相关的插件,那么简直就是神器,但有时会有Bug需要自己处理,呜呜。

不推荐 pyenv 的理由

  • 它用来解决切换Python版本问题,其实只不过是切换虚拟环境的另外一种途径,并没有存在的必要性
  • 如果同Python版本多个虚拟环境情况它是没有办法解决的,还得用虚拟环境,不如一开始都统一用虚拟环境
  • Python社区无感,它不是官方接受的解决方案,没有社区强力支持

文章作者: Escape
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Escape !
  目录