坑边闲话:闲话

包管理器(package manager)和项目管理器(project manager)是两种非常重要的开发管理工具,前者可以保证我们对自己的开发环境有完整的掌控能力,后者可以保证我们对手头的项目有十足的把握能力。但是很多开发者,特别是新手,在初次接触这两个名词时不知所措,我分析来分析去,得出一个结论:他们被日益傻瓜化的封装给绑架了,所以他们看不到这两样东西的重要性。那么接下来的几篇文章,我会详细介绍这两种管理器的一般用法和设计理念,以帮助新手入门。

我的目标是希望新手读者看完文章后,能了解包管理系统、项目管理系统的运作原理,不再完全依赖 Visual Studio 等高度封装的 IDE,写出小而精的项目,令系统的硬盘冗余达到最小。

1. 什么是包管理器·

包管理器与包的开发、安装、更新密切相关。如果一个开发环境所具有的包非常少,那么你不需要包管理器。但是很多时候,随着一个系统的发展,各种各样的包也随之出现,手动管理不再可能。比如 Debian 系列的 Linux 的软件包越来越多,社区急需一个可以管理整个系统的管理器,它可以负责安装新的包、更新旧的包,自动解决包的依赖问题。其中,包的依赖关系十分令人头疼,很多时候安装 A 需要先安装 B,而安装 B 需要安装 C 和 D,这令人无法接受,所以理想的包管理器应该能够自动生成依赖的关系树。

我们常见的包管理器有以下的几个:

  • apt,这是 Debian 系列 Linux 发行版的系统级的软件管理器
  • Cygwin-setup.exe,没错,这是 Cygwin 的包管理器,比较愚蠢的一个非命令行软件(可以安装一个脚本,类似 apt)
  • tlmgr,这是 texlive 的包管理器,管理各种 LaTeX 的宏包
  • Pypi,或者简称 pip,这是 Python 的包管理器,管理各种 Python 的包,有时候你 import一个库时用不了,系统说你没有安装这个包,那你就需要 pip 了;pip 也和 Python 一样,分 2 版本和 3 版本,其分别针对 Python2 和 Python3
  • homebrew,这是一个在 macOS 上运行的包管理器,可以管理 macOS 的很多包。因为苹果并没有自己的包管理软件

综上,我们可以总结一下,当可用的包很多时,就需要包管理器了。包管理器需要与镜像服务器联系,以解决问题。

  1. 询问服务器,你有没有我需要的那个包?如果服务器说有,那么你可以下载,然后本地安装;
  2. 询问服务器我的这个安装包是不是过时了,能否更新?服务器可以对比版本,如果有新版本,就提示你要升级了。
  3. 一般本地包管理器通过网络与镜像服务器联系,如果镜像服务器在国外,那么很有可能你的连接速度、下载速度非常慢,这个时候我们最好换国内的镜像服务器。某些大型公司,如阿里、腾讯、网易等,会对公众提供免费的镜像,而很多知名的大学,如清华大学、中国科大,也会对外提供镜像服务。
  4. 以清华 tuna 举例,它不仅仅提供某个领域的镜像服务,而且似乎全领域服务,它提供 Python 镜像、apt 镜像、tlmgr 镜像,简直无所不包。新手最应该把这个镜像网站的脉络摸清楚。

2. 介绍一下 Pypi·

Pypi 是 Python 的包管理器上游仓库。

如你需要下载一个 numpy,那就需要运行

1
pip install numpy

值得注意的是,用 pip 安装 python 包虽然简单,但是也存在一部分历史遗留的问题。比如它对某些包的 metadata 并不能充分获取,而且有时候哪怕充分获取了,也不足以干净利落地管理这些包。鄙人今天花了点时间研究了一下这方面的内容,有两个问题想谈一谈。

  • 首先是 pip 的问题,pip 作为一个针对 Python 的包管理器,同 apttlmgrnpm 等包管理器具有相似的功能,比如安装某个包(pip install <PACKAGE>)查看本管理器所管理的软件有哪些(pip list),比如搜索镜像服务器中有哪些包(pip search <KEYWORDS>),比如列出哪些包过时了需要更新一下(pip list --outdated),再比如升级一下需要更新的某个包(pip install -U <PACKAGE>)等。这里的 pip 指的是 pip2,管理的是 Python2 的包,你可以替换成 pip3,以管理 Python3 的包。

  • 你要知道,有些时候你并不能用 pip 管理所有的包。有时候会遇到以下这种问题。不要着急,也不要忽略旧版本直接升级(这有很大害处!)

1
Cannot uninstall ‘nibabel’. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.

3. 清华 Pypi 镜像·

3.1 临时使用清华镜像来安装某个 Python3 的包:·

1
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple <PACKAGE>

注意:

  • simple 不能少
  • https 而不是 http

3.2 将清华镜像设为默认·

升级 pip 到最新的版本 (>=10.0.0) 后进行配置:

版本升级,并修改 pip 的链接文件中的代码:

1
2
pip install -U pip
sudo vim /usr/bin/pip

把如下三行

1
2
3
from pip import main
if __name__ == '__main__':
sys.exit(main())

改为如下形式:

1
2
3
from pip import __main__
if __name__ == '__main__':
sys.exit(__main__._main())

同理改正 pip3sudo vim /usr/bin/pip3)。

最后在命令行里更改 pipconfigure 文件:

1
2
pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

如果你的 pip 默认源的网络连接较差,可临时使用清华镜像站来升级 pip

1
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pip -U

pip 的利用方式有很多,有很多可选的选项,如列出所有需要更新的 python 包,然后更新某个名为< package_name >的包,命令如下:

1
2
pip3 list --outdated
pip3 install --upgrade <PACKAGE>

如同 tlmgr 管理所有的 texlive 的宏包一样,pip 也可以管理所有的 Python 库,但是并没有内置的「一个命令升级所有 Python 包」的内置选项,在此鄙人介绍一个简单的 bash 命令:

1
pip3 list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1  | xargs -n1 pip install -U

可以把这个命令起一个别名,然后保存在 bashrc 里,以后直接调用 pip3-upgrade-all 就好,如下所示:

1
alias pip3-upgrade-all='pip3 list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1  | xargs -n1 sudo pip install -U'

该命令原理如下:我们知道,单纯运行 pip3 list --outdated --format=freeze 并不会得到一个很显然、很干净的结果,但是我们希望可以得到一个字符串,这个字符串含有所有的待升级 Python 包,而且包与包彼此之间用空格分割。但是系统并没有内置命令。好在可以通过 bash 编程做到这一点。

首先把输出通过 pipe 管道传给了 grep,让 grep 做一下切割,切割完之后,很多不需要的信息就不存在了;然后再把 grep 的输出通过 pipe 管道传给 cut,做二次切割,-d = 是一个完整的命令,指的是要用等号作为分隔符号进行列划分,-f 是指现实哪几列,我们的参数是 1,所以现实第一列。值得注意的是,这里的 -f 是从 1 开始的,而不是编程中常见的 0;cut 切割完的输出已经是我们的理想字符串了,我们把这个终极字符串,通过 xargs 命令传给 pip install --upgrade(或者 pip install -U,这个 -U 就是指 --upgrade)。

如此就完成了 Pypi 的初步教程。注意,在 Linux 系统上,运行 Pypi 可能需要 root 权限,这时候应该使用 sudo pip 来执行你的操作。

接下来的几篇文章我会介绍其他的包管理器、项目管理器的使用方法。