什么是wheel包,如何去用它
前言
学习Python的人一定知道pip install xxx
的作用,但是很少有人会去想背后的东西——pip安装的到底是什么?什么是.whl包?如果哪天我自己想写一个python包,应该怎么做?
一、初探pip安装过程
我们先来看两个pip安装包的过程。第一个是再熟悉不过的NumPy包:
$ pip install numpy
Collecting numpy
Downloading numpy-1.19.4-cp36-cp36m-manylinux2010_x86_64.whl (14.5 MB)
|████████████████████████████████| 14.5 MB 15.1 MB/s
Installing collected packages: numpy
Successfully installed numpy-1.19.4
一个是uWSGI包(用来做什么的不用管)
$ pip install uwsgi
Collecting uwsgi
Downloading uWSGI-2.0.19.1.tar.gz (803 kB)
|████████████████████████████████| 803 kB 274 kB/s
Building wheels for collected packages: uwsgi
Building wheel for uwsgi (setup.py) ... done
Created wheel for uwsgi: filename=uWSGI-2.0.19.1-cp36-cp36m-linux_x86_64.whl size=597269 sha256=f2289db3db6d625f136f794be0319da477ad740eb28cd063971f8d335327c09c
Stored in directory: /private/.cache/pip/wheels/3a/7a/c1/492f02e0cde1e39f8b75c79dc5ef3b7e2c93b02e3a7eaabe0c
Successfully built uwsgi
Installing collected packages: uwsgi
Successfully installed uwsgi-2.0.19.1
细看会发现:
- NumPy下载的是以whl格式结尾的东西,而uWSGI则是下了tar.gz的压缩包;
- uWSGI多了一些build的过程,在build结束之后才开始install。
了解了这些区别之后,我们再来铺垫一点发行版的内容。
二、包的发行版(Distribution)
2.1. 什么是发行包?(Distribution Package)
发行包,说得直白一点就是端用户(比如一个刚开始学Python的小白)下载的东西。一个发行包包含了模组、资源文件等等。用户们下载好发行包就能直接安装使用了。
2.2. 什么是源发行版?(Source Distribution/sdist)
源发行版,顾名思义就是“发行的是源码”,包含了元数据和源码文件(Python, C++等),必须要经过编译才能使用起来。编译的过程是在用户的机子上进行的。通常用python setup.py sdist
来产生源发行版。
2.3. 什么是已编译的发行版?(Built Distribution)
同样地,如果源码被编译好了,那就成了一个已编译的发行版了。我们要谈到的wheel就是已编译发行版的一种格式。有的地方提到二进制发行版(binary distribution)也基本默认是wheel包。(下文如果提到wheel或二进制发行版就默认是已编译发行版了)
所以,结合上面的例子,我们大概可以猜出来NumPy和uWSGI安装上的区别了:NumPy下载的是已编译的发行版,而uWSGI下载的是源发行版,在本地编译完了之后才执行安装。
所以为什么有的包直接提供了wheel,有些包要提供源码呢?
2.3.1 用户角度
从用户的角度看,wheel包显然就是方便的代名词。能用wheel的时候我们尽量就不要去用别的安装方式了,免得给自己找麻烦。
为了体现使用whl包到底比源码包快多少,我们可以尝试下面两种不同的过程来安装NumPy:
# 使用源码包安装,即便有PyPi上有whl包可以使用
# --no-cache-dir:不适用本地缓存,始终从PyPi上面下载
# --force-reinstall:强制重新安装,即便这个包在系统里已经最新
# --no-binary=:all: :不允许使用二进制包,:all:表示对所有相关的依赖包也采用同样的选择
time python -m pip install --no-cache-dir --force-reinstall --no-binary=:all: numpy
# 使用whl包安装
# --only-binary=numpy:指定针对numpy使用二进制包(即已编译好的发行版)
time python -m pip install -no-cache-dir --force-reinstall --only-binary=numpy numpy
在采用第一种方式的时候,你还很有可能遇到报错,说有一些依赖的头文件没找到,这也从侧面凸显了whl的好处:不用怎么担心依赖关系。
2.3.2 开发者角度
从开发者的角度来看,提供whl或是源发行包则取决于对项目复杂性、相互依赖关系以及其他因素的综合考量。如果要提供whl包,那就要针对不同的平台都要准备,对兼容性要有比较全面的考虑。
三、wheel(.whl)包到底是什么
wheel包本质上是一个zip文件。是已编译发行版的一种格式。需要注意的是,尽管它是已经编译好的,包里面一般不包含.pyc
或是Python字节码。一个wheel包的文件名由以下这些部分组成:
{dist}-{version}(-{build})?-{python}-{abi}-{platform}.whl
举个例子:
tensorflow-2.3.1-cp36-cp36m-macosx_10_9_x86_64.whl
- tensorflow是包名(dist)。
- 2.3.1是包的版本号(version)。
- cp36是对python解释器和版本的要求(python)。cp指的是CPython解释器,35指的是版本3.5的Python。比如你拿JPython解释器,这个包就不能用了。
- cp36m是ABI的标签(python)。ABI即应用二进制接口(Application Binary Interface)。这个一般来说我们不用关心。
- macosx_10_9_x86_64是平台标签(platform),告诉我们这个包是为macOS操作系统的,使用10.9的macOS developer SDK编译,适用于x86-64指令集。
再举个例子:
requests-2.7.0-py2.py3-none-any.whl (470.6 kB)
- py2.py3告诉我们这个包对Python2和3都能支持。
- none是ABI标签,在这里是没有,也不用考虑。
- any是平台,意味着这个包在任何平台上都能用。
- 一般如果遇上py2.py3-none-any结尾的whl包,那说明这个包是通用wheel包(universal wheel)——对Python版本、ABI和平台都没有要求。
还有个例子:
tensorflow-2.3.1-cp35-cp35m-manylinux2010_x86_64.whl
- manylinux是一个比较有意思的平台标签。鉴于Linux系统有不同的发行版(Ubuntu,CentOS,Fedora等等),而你的包里有需要编译的C/C++代码,那有可能不同Linux发行版就不能运行你的包了,而为每个Linux发行版生成一个wheel又太麻烦,所以就诞生了manylinux系列标签:
manylinux1
(PEP513),manylinux2010
(PEP571)和manylinux2014
(PEP599)。 - manylinux标签的核心是一个CentOS的Docker镜像,打包了一些编译器套件、多版本Python和pip、动态库等来确保兼容性。这个在PEP513里面有提到。
四、wheel包的好处
- 安装快;
- 一般比源发行版体积小很多。比方说matplotlib,它的wheel包最大只有11.6MB,而源码却有37.9MB。网络传输的压力显然是wheel小;
- 免除了_setup.py_的执行。setup.py是Python的disutils、setuptools编译机制所需要的文件,需要执行大量的编译、安装工作,如果没有wheel的存在,包的安装、维护都会很麻烦;
- 不需要编译器。pip在下载时帮你确定了版本、平台等信息;
- 使用wheel的话,pip自动为你生成.pyc文件,方便Python解释器的读取。
总结
以上就是作为一个普通用户需要了解的关于wheel包的知识了。下一篇(如果没有放鸽子的话)打算来谈一谈作为开发者如何去创建、发布你的wheel包。